I am having trouble binding users that are not the root dn in OpenLDAP, even if I immediately set the password, I still get ldap_bind: Invalid credentials (49)
For example, if I use ldappasswd to set the password (authenticating with the root dn), and then immediately use ldapwhoami to try to authenticate, I get the following error:
leif#nixos ~ $ ldappasswd -x -D cn=Admin,dc=leifandersen,dc=net -W -s badpasswd cn=leiftest,ou=users,dc=leif,dc=net
Enter LDAP Password:
leif#nixos ~ $ ldapwhoami -x -w badpasswd -D cn=leiftest,ou=users,dc=leifandersen,dc=pl
ldap_bind: Invalid credentials (49)
(Note that in this case slapd is running on localhost and port 389, so I don't seem to need to specify those.)
I am using ppolicy, configured as:
# ppolicy, leifandersen.net
dn: cn=ppolicy,dc=leifandersen,dc=net
objectClass: device
objectClass: pwdPolicyChecker
objectClass: pwdPolicy
cn: ppolicy
pwdAllowUserChange: TRUE
pwdAttribute: userPassword
pwdCheckQuality: 1
pwdExpireWarning: 600
pwdFailureCountInterval: 30
pwdGraceAuthNLimit: 5
pwdInHistory: 5
pwdMaxAge: 0
pwdMaxFailure: 5
pwdMinAge: 0
pwdMinLength: 5
pwdMustChange: FALSE
pwdSafeModify: FALSE
pwdLockoutDuration: 30
pwdLockout: FALSE
And set up my initial configuration of OpenLDAP with nix, the relevant bit (I think) being:
"olcOverlay=ppolicy" = {
attrs = {
objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
olcOverlay = "ppolicy";
olcPPolicyDefault = "cn=ppolicy,dc=leif,dc=pl";
olcPPolicyUseLockout = "FALSE";
olcPPolicyHashCleartext = "TRUE";
};
};
the whole config being:
services.openldap = {
enable = true;
settings = {
attrs.olcLogLevel = [ "stats" ];
children = {
"cn=schema".includes = [
"${pkgs.openldap}/etc/schema/core.ldif"
"${pkgs.openldap}/etc/schema/cosine.ldif"
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
"${pkgs.openldap}/etc/schema/nis.ldif"
"${pkgs.openldap}/etc/schema/ppolicy.ldif"
];
"olcDatabase={-1}frontend" = {
attrs = {
objectClass = "olcDatabaseConfig";
olcDatabase = "{-1}frontend";
olcAccess = [ "{0}to * by dn.exact=uidNumber=0+gidNumber=0,cn=peercred,cn=external,cn=auth manage stop by * none stop" ];
};
};
"olcDatabase={0}config" = {
attrs = {
objectClass = "olcDatabaseConfig";
olcDatabase = "{0}config";
olcAccess = [ "{0}to * by * none break" ];
};
};
"olcDatabase={1}mdb" = {
attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{1}mdb";
olcDbDirectory = "/var/db/ldap";
olcDbIndex = [
"objectClass eq"
"cn pres,eq"
"uid pres,eq"
"sn pres,eq,subany"
];
olcSuffix = "dc=leifandersen,dc=net";
olcAccess = [ "{0}to * by * none break" ]; # read break for readable
olcRootDN = "cn=Admin,dc=leifandersen,dc=net";
olcRootPW = "{SSHA}<SOMEHASH>";
};
children = {
"olcOverlay=ppolicy" = {
attrs = {
objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
olcOverlay = "ppolicy";
olcPPolicyDefault = "cn=ppolicy,dc=leif,dc=pl";
olcPPolicyUseLockout = "FALSE";
olcPPolicyHashCleartext = "TRUE";
};
};
};
};
};
};
};
Does anyone have any idea why I'm getting an invalid credentials error? Is there something wrong with my ppolicy setup? (I know its not as strict as it should be, I laxed the requirements a bit in the hopes I could get something to work. Also sorry if this is a bad question, I'm still very new to ldap and my previous searches didn't turn up any answer.)
OpenLDAP requires during authentication binds that access be explicitly granted for the necessary attributes (particularly userPassword) to be used during authentication by the state of the bind before authentication (i.e. anonymous). Try changing the line
olcAccess = [ "{0}to * by * none break" ]; # read break for readable
to
olcAccess = [
"{0}to attr=userPassword by anonymous auth"
"{1}to * by * none break"
]; # read break for readable
in the "{1}mdb" section, as suggested here.
Related
I need to collect information about total and assigned licenses programmatically.
The way that is described here: https://tech.nicolonsky.ch/manage-azure-ad-group-based-licensing-with-powershell/ - does not work on AzureUSGovernment environment. The following error occurs: ""Get-AADLicenseSku : AADSTS900382: Confidential Client is not supported in Cross Cloud request."
So, I am looking for a way to adjust it and use it on AzureUSGovernment. But I could not find the resource ID of main.iam.ad.ext.azure.us
As I understood, the ID of main.iam.ad.ext.azure.com is 74658136-14ec-4630-ad9b-26e160ff0f. But I do not understand where it is coming from.
Thank you in advance for the help.
I created a script based on the original scripts:
$context = Get-AzContext
if ($null -eq $context) {
$null = Connect-AZAccount -EA stop
$context = Get-AzContext
}
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "https://main.iam.ad.ext.azure.us")
$header = #{
'Authorization' = 'Bearer ' + $apiToken.AccessToken.ToString()
'Content-Type' = 'application/json'
'X-Requested-With' = 'XMLHttpRequest'
'x-ms-client-request-id' = [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
Write-Verbose "Connected to tenant: '$($context.Tenant.Id)' as: '$($context.Account)'"
$baseUrl = "https://main.iam.ad.ext.azure.us/api/"
try {
$request = Invoke-WebRequest -Method Get -Uri $($baseUrl + "AccountSkus") -Headers $header
$requestContent = $request | ConvertFrom-Json
return $requestContent
}
catch {
# convert the error message if it appears to be JSON
if ($_.ErrorDetails.Message -like "{`"Classname*") {
$local:errmsg = $_.ErrorDetails.Message | ConvertFrom-Json
if ($local:errmsg.Clientdata.operationresults.details) {
Write-Error $local:errmsg.Clientdata.operationresults.details
}
else {
Write-Error $local:errmsg
}
}
else {
Write-Error $_
}
}
But it fails with the following error:
"Invoke-RestMethod :
401 - Unauthorized: Access is denied due to invalid credentials.
Server Error
401 - Unauthorized: Access is denied due to invalid credentials.
You do not have permission to view this directory or page using the credentials that you supplied."
I tried to use the user account and service principal. Global Admin role is assigned to both.
I found the solution. In my script above ^^, I replaced
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "https://main.iam.ad.ext.azure.us")
with
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "ee62de39-b9b0-4886-aa58-08b89c4e3db3")
And now it works. Here is the example of the response:
name : Office 365 E3 - GCCHIGH
accountId : XXXXXX-XXXX-4427-8719-XXXXXXXXXXXX
accountSkuId : TEST:ENTERPRISEPACK_USGOV_GCCHIGH
availableUnits : 0
totalUnits : 1
consumedUnits : 1
skuId : aea38a85-XXXX-XXXX-aa00-XXXXXXXXXXXX
isDepartment : False
warningUnits : 0
serviceStatuses : {#{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}...}
There is something that I am not understanding about terraform variables. I am getting prompted for two variables in when I run "terraform apply". I don't think that I should be prompted for any as I defined a terraform.tfvars. I am getting prompted for (applicationNamespace, and staticIpName) but I am not sure why. What am I misunderstanding?
I created a file (terraform.tfvars):
#--------------------------------------------------------------
# General
#--------------------------------------------------------------
cluster = "reddiyo-development"
project = "<MYPROJECTID>"
region = "us-central1"
credentialsLocation = "<MYCERTLOCATION>"
bucket = "reddiyo-terraform-state"
vpcLocation = "us-central1-b"
network = "default"
staticIpName = "dev-env-ip"
#--------------------------------------------------------------
# Specific To NODE
#--------------------------------------------------------------
terraformPrefix = "development"
mainNodeName = "primary-pool"
nodeMachineType = "n1-standard-1"
#--------------------------------------------------------------
# Specific To Application
#--------------------------------------------------------------
applicationNamespace = "application"
I also have a terrform script:
variable "cluster" {}
variable "project" {}
variable "region" {}
variable "bucket" {}
variable "terraformPrefix" {}
variable "mainNodeName" {}
variable "vpcLocation" {}
variable "nodeMachineType" {}
variable "credentialsLocation" {}
variable "network" {}
variable "applicationNamespace" {}
variable "staticIpName" {}
data "terraform_remote_state" "remote" {
backend = "gcs"
config = {
bucket = "${var.bucket}"
prefix = "${var.terraformPrefix}"
}
}
provider "google" {
//This needs to be updated to wherever you put your credentials
credentials = "${file("${var.credentialsLocation}")}"
project = "${var.project}"
region = "${var.region}"
}
resource "google_container_cluster" "gke-cluster" {
name = "${var.cluster}"
network = "${var.network}"
location = "${var.vpcLocation}"
remove_default_node_pool = true
# node_pool {
# name = "${var.mainNodeName}"
# }
node_locations = [
"us-central1-a",
"us-central1-f"
]
//Get your credentials for the newly created cluster so that microservices can be deployed
provisioner "local-exec" {
command = "gcloud config set project ${var.project}"
}
provisioner "local-exec" {
command = "gcloud container clusters get-credentials ${var.cluster} --zone ${var.vpcLocation}"
}
}
resource "google_container_node_pool" "primary_pool" {
name = "${var.mainNodeName}"
cluster = "${var.cluster}"
location = "${var.vpcLocation}"
node_count = "2"
node_config {
machine_type = "${var.nodeMachineType}"
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
"https://www.googleapis.com/auth/trace.append",
]
}
management {
auto_repair = true
auto_upgrade = true
}
autoscaling {
min_node_count = 2
max_node_count = 10
}
}
# //Reserve a Static IP
resource "google_compute_address" "ip_address" {
name = "${var.staticIpName}"
}
//Install Ambassador
module "ambassador" {
source = "modules/ambassador"
applicationNamespace = "${var.applicationNamespace}"
}
You can try to force it to read your variables by using:
terraform apply -var-file=<path_to_your_vars>
For reference, read below, if anybody face the similar issue.
“terraform.tfvars” is the default variable file name, from where terraform will read variables.
If any other file name is used, it needs to be passed in the command line i.e: “terraform plan –var=whateverName.tfvars
Also, order of Loading for variables by Terraform program.
Environment Variables
terraform.tfvars
terraform.tfvars.json
Any .auto.tfvars
Any –var or –var-file options
I have been trying to dump my Kerberos database (ldap backend) using kdb5_util dump (filename), but I get:
kdb5_util load_dump version 6
kdb5_util: error performing Kerberos version 5 release 1.8 dump (Server error)
policy default 0 0 1 1 1 0 0 0 0
Kerberos KDC and Kadmin log has nothing, ldap.log gives
May 31 12:40:17 kdc slapd[28020]: connection_input: conn=1091 deferring operation: binding
Everything else works fine, creating, deleting, authentication of principals, no problem. Just dumping the DB fails. As far as I understand, the backend should not have any influence on the dump.
Any ideas how I can debug or fix this? What am I missing?
/etc/krb5.conf
[libdefaults]
default_realm = REALM.EXAMPLE.COM
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
[realms]
REALM.EXAMPLE.COM = {
kdc = kdc.realm.example.com
admin_server = kdc.realm.example.com
kpasswd_server = kdc.realm.example.com
}
[domain_realm]
.realm.example.com = REALM.EXAMPLE.COM
/etc/krb5kdc/kdc.conf
[realms]
REALM.EXAMPLE.COM = {
default_domain = realm.example.com
database_module = ldapconf
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /etc/krb5kdc/.master
max_life = 10h 0m 0s
max_renewable_life = 7d 0h 0m 0s
master_key_type = aes256-cts
supported_enctypes = aes256-cts-hmac-sha1-96:normal
#aes128-cts-hmac-sha1-96:normal arcfour-hmac:normal
default_principal_flags = +preauth
pkinit_identity = FILE:/etc/krb5kdc/kdc-cert.pem,/etc/krb5kdc/.kdc-key.pem
pkinit_anchors = FILE:/etc/krb5kdc/ca-cert.pem
dict_file = /root/bad_passwords.dict
}
[dbmodules]
ldapconf = {
db_library = kldap
ldap_kerberos_container_dn = "cn=kerberos,dc=realm,dc=example,dc=com"
ldap_kdc_dn = "cn=kerberos-kdc,dc=realm,dc=example,dc=com"
ldap_kadmind_dn = "cn=kerberos-admin,dc=realm,dc=example,dc=com"
ldap_servers = ldapi:///
ldap_service_password_file = /etc/krb5kdc/.service
}
[logging]
kdc = FILE:/var/log/kerberos/kdc.log
admin_server = FILE:/var/log/kerberos/kadmin.log
default = FILE:/var/log/kerberos/kerberos.log
Found the Problem after debugging at last:
The LDAP backend has a hard Size limit of 500 for search requests. With 501 Users that bit me in the backside!
Fix:
#
# remove sizelimit for ldap search
#
# apply with ldapmodify -Y EXTERNAL -H ldapi:/// -f sizelimit.ldif
#
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcLimits
olcLimits: dn.exact="cn=kerberos-admin,dc=realm,dc=example,dc=com" size=unlimited
Apply, restart slapd, and dump happily away
We migrated our Magento site (Magento 1.8.1.0) from old server to a new server.
But, we can't use the Winepos integration extension any more.
Our site is connected with Winepos system, and this Magento extension had been operated before migrating work.
This is Winepos API manual.
At this time, we think some PHP modules were not installed on our new server.
But, we don't know which PHP modules were not installed. It seems all PHP modules were installed on our new server.
The Magento extension to integrate with Winepos are as follow. This extension is consisted with two files.
Config.xml
<config>
<global>
<events>
<checkout_onepage_controller_success_action>
<observers>
<igor_winepos_order_success_observer>
<type>singleton</type>
<class>igor_Winepos_Model_Wineposobserver</class>
<method>checkoutSuccessObserve</method>
</igor_winepos_order_success_observer>
</observers>
</checkout_onepage_controller_success_action>
</events>
</global>
</config>
wineposobserver.php
class igor_Winepos_Model_Wineposobserver extends Varien_Event_Observer {
function customlog($obj) {
ob_start();
var_dump($obj);
$out1 = ob_get_contents();
ob_end_clean();
$f = fopen('/tmp/log.txt', 'ab');
fwrite($f, $out1);
fclose($f);
}
public function __construct() {
}
public function checkoutSuccessObserve($observer) {
// $event = $observer->getEvent();
$order_ids = $observer->getData('order_ids');
if(gettype($order_ids) == 'array' && count($order_ids) == 1) {
$the_order = Mage::getModel('sales/order')->load($order_ids[0]);
Mage::helper('globalfunc')->registerOrderWithWinePOSAsynchronousWithTimeout($the_order);
}
}
}
I am getting the following in the Apache log:
PHP Warning: PHP Startup: apc.shm_segments setting ignored in MMAP mode in Unknown on line 0 [Sun Apr 30 06:32:30 2017] [notice] Apache/2.2.22 (Ubuntu) mod_ssl/2.2.22 OpenSSL/1.0.1 configured -- resuming normal operations
285: function registerOrderWithWinePOSAsynchronousWithTimeout($the_order) {
286 try {
287 $items = $the_order->getAllItems();
...
426 $ordered_raw_item = $ordered_products_raw_items[$ordered_product_id];
428: $product_winepos_id = trim(strval($product->getResource()->getAttribute('winepos_id')->getFrontend()->getValue($product)));
429
430 $item_element = $doc->createElement('item');
432 $item_num_element = $doc->createElement('item-num');
433: $item_num_element->appendChild($doc->createTextNode(strval($product_winepos_id)));
434 $item_element->appendChild($item_num_element);
...
466 $the_xml = $doc->saveXML();
468: // $post_result = Mage::helper('globalfunc')->post_to_api_winepos('https://wines-in-november.vznlink.com/orders', $the_xml, 'admin276975', '8dc670fb943dc2c0a1415405cdf00e3ec579c4e6', 8, 10);
470: return Mage::helper('globalfunc')->delayed_post_to_winepos($the_xml);
471 } catch(Exception $e) {
472 $this->customlog($e);
...
475 }
I created a new module to integrate with POS system and implemented inventory synchronization successfully.
This work should be done by cron job. To do it, I created three individual script files.
Also, POS provider should provide ordered product information including inventory information as txt file via ftp in every time interval.
lftp -u [username],[password] -e'set ftp:passive-mode false; cd files; put data.txt; quit' [folder name]
The cron job operating blocks are as follow.
cron_file_mover.php
$start = microtime(true);
shell_exec('cp /home/files/data.txt /var/www/vhosts/magento/');
shell_exec('chown -R www-data:www-data /var/www/vhosts/magento/');
$end = microtime(true);
echo 'Run time: '.round($end-$start, 4).'s';
cron_pos_post_script.php
Utils::initMagento(); $MAX_RETRIES = 5;
$delayed_jobs = Utils::mageGetRows("select * from delayed_jobs where job_type = 'pos_order' and status = 'todo' order by created_at DESC");
$current_index = 0;
foreach($delayed_jobs as $delayed_job) {
$current_index += 1;
$retry_count = intval($delayed_job['retry_count']);
$retry_count += 1;
$post_result = Mage::helper('globalfunc')->post_to_api_pos('https://vznlink.com/orders', $delayed_job['job_details'], 'admin', 'fd93d2de58ab', 8, 10);
Utils::mageSqlExecute("update delayed_jobs set status = 'done' where id = " . $delayed_job['id']);
Utils::mageSqlExecute("update delayed_jobs set status = '" . $new_status . "', retry_count = " . $retry_count . " where id = " . $delayed_job['id']);
}
cron_pos_update_script.php
$PATH_TO_FILE = '/var/www/vhosts/magento/data.txt';
function read_pos_file($path_to_file) {
$min_count_required_for_product = 12;
$f = fopen($path_to_file, 'rb');
$text = trim(fread($f, 100000000));
fclose($f);
$lines = preg_split('/\r\n|\r|\n/i', $text);
$products = array();
foreach($lines as $line) {
$product = preg_split('/\t/i', trim($line));
if(count($product) >= $min_count_required_for_product) {
$product[0] = strval(trim($product[0]));
$product[1] = strval(trim($product[1]));
$product[2] = strval(trim($product[2]));
$product[11] = intval(strval(trim($product[11])));
$products []= $product;
}
}
return $products;
}
function update_stock_for_stock_item($product_id, $new_stock) {
$stock_item = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product_id);
$stock_item->setData('qty', $new_stock);
if($new_stock > 0) {
$stock_item->setData('is_in_stock', 1);
}
$stock_item->save();
}
$products = read_pos_file($PATH_TO_FILE);
$total_count = 0; $processed_count = 0;
foreach($products as $product) {
$total_count += 1;
$item_number = $product[0];
$new_stock = $product[11];
if($new_stock < 0) {
$new_stock = 0;
}
$products_matching_item_number = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('pos_id')->addFieldToFilter('pos_id', $item_number)->getItems();
if(count($products_matching_item_number) == 1) {
$products_matching_item_number = array_values($products_matching_item_number);
$matching_product = $products_matching_item_number[0];
$matching_product_id = $matching_product->getId();
update_stock_for_stock_item($matching_product_id, $new_stock);
$processed_count += 1;
}
}
The cron job settings are as follows.
10 * * * * /usr/bin/php /var/www/vhosts/magento/pos/cron_winepos_post_script.php &> /dev/null
10 * * * * /usr/bin/php /var/www/vhosts/magento/pos/cron_winepos_update_script.php &> /dev/null
20 * * * * /usr/bin/php /var/www/vhosts/magento/pos/cron_pos_file_mover.php &> /dev/null
Keep in mind, checking POS data provided from POS system, cron job setting, checking update script operations.
I'm trying to connect to the Asana api with the Faraday gem. My http request works using curl but I get a completely different response with Faraday. What did I do wrong:
require 'faraday'
require 'base64'
require 'net/https'
module Asana
def self.get_tasks(project_asana_id)
conn = create_connection
response = conn.get "/tasks", {"project" => 14790966979027}, {"Content-Type" => "text/plain", "User-Agent"=>"Chrome/25.0.1364.160", "Accept" => "application/json"}
p response.body
end
def self.create_connection
Faraday.new(:url => ENV['ASANA_URL']) do |faraday|
faraday.request :url_encoded # form-encode POST params
faraday.response :logger # log requests to STDOUT
faraday.request :basic_auth, ENV['ASANA_API_KEY'], ''
faraday.adapter :net_http # make requests with Net::HTTP
end
end
end
My response:
"<html>\n<head>\n<title>Asana - Log In</title><script>__FILE__=\"(none)\";var config = {\n\"CLUSTER\": \"prod\",\n\"PRETTY_JS_CODEGEN\": false\n};</script><link rel=\"shortcut icon\" href=\"/-/static/luna/browser/images/favicon.ico\" />\n\n<script type=\"text/javascript\" src=\"https://use.typekit.com/sli4yxq.js\"></script><script>__FILE__=\"(none)\";try{Typekit.load();}catch(e){}</script>\n<script>__FILE__=\"(none)\";req = reqLazy = function(file_path) { var m = modules[file_path]; return (m !== undefined) ? m.exports : undefined; }; modules = {}; globals = window;</script>\n<link type=\"text/css\" rel=\"stylesheet\" href=\"/-/static/build/css/apps/asana/css/dialog_page_root.css\" />\n<link type=\"text/css\" rel=\"stylesheet\" href=\"/-/static/build/css/apps/asana/css/login.css\" />\n<script>__FILE__=\"3rdparty/mochikit/MochiKit/Base.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/3rdparty/mochikit/MochiKit/Base.js\" charset=\"utf-8\"></script>\n<script>__FILE__=\"3rdparty/mochikit/MochiKit/Iter.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/3rdparty/mochikit/MochiKit/Iter.js\" charset=\"utf-8\"></script>\n<script>__FILE__=\"3rdparty/mochikit/MochiKit/Async.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/3rdparty/mochikit/MochiKit/Async.js\" charset=\"utf-8\"></script>\n<script>__FILE__=\"3rdparty/mochikit/MochiKit/DOM.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/3rdparty/mochikit/MochiKit/DOM.js\" charset=\"utf-8\"></script>\n<script>__FILE__=\"3rdparty/mochikit/MochiKit/Style.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/3rdparty/mochikit/MochiKit/Style.js\" charset=\"utf-8\"></script>\n<script>__FILE__=\"luna/common/lib/minimochi.js\";module = { id: __FILE__, exports: {} };exports = module.exports;modules[module.id] = module;</script>\n<script type=\"text/javascript\" src=\"/-/static/luna/common/lib/minimochi.js\" charset=\"utf-8\"></script>\n</head>\n<body>\n<!-- DEBUG-TAG: login -->\n<div id=\"Flags\" class=\"enable_experiment_enrollment_logging enable_delete_session_objects enable_oauth enable_new_billing_page enable_expanded_team_browser log_api_metrics_to_graphite enable_separator_between_far_apart_stories enable_mobile_sections enable_rph_search enable_rph_search_topbar enable_fast_query_dependencies enable_list_caching enable_reset_view_state_on_switches enable_task_caching enable_asanarama enable_key_combos enable_promote_tasks_with_due_dates enable_sort_by_alpha enable_hypertext_ime enable_spooky_theme enable_winter_theme enable_google_tags_iframe enable_box_attachments enable_hacks enable_sync_without_promises\"><script type=\"text/javascript\" src=\"/-/static/luna/browser/browser_google_auth.js\"></script><script>BrowserGoogleAuth.init();</script><script type=\"text/javascript\" src=\"/-/static/luna/browser/dialog.js/?v=3\"></script><div id=\"dialog_wrapper\"><div id=\"bg_pattern\" class=\"bg-pattern\"></div><script>\n if (navigator.userAgent.indexOf('WebKit') > 0) {\n document.getElementById(\"dialog_wrapper\").className += ' webkit';\n }\nDialog.event_name_to_url[\"BrowserLogin-GoogleAuth-Redirect\"] = \"https://app.asana.com/app/asana/-/log?se=%7B%22name%22%3A%22BrowserLogin-GoogleAuth-Redirect%22%2C%22user_agent%22%3A%22Chrome%2F25.0.1364.160%22%7D&cb=1409177825638&hash=08fa47a0cd77d30807017aea9003b9803d22525eb78f4af4f51d8de4c29094ff\";\nDialog.event_name_to_url[\"BrowserLogin-GoogleAuth-Popup-Start\"] = \"https://app.asana.com/app/asana/-/log?se=%7B%22name%22%3A%22BrowserLogin-GoogleAuth-Popup-Start%22%2C%22user_agent%22%3A%22Chrome%2F25.0.1364.160%22%7D&cb=1409177825642&hash=ee5cf64e6b5c72f7c8ba4bf62dd63adf5974fba3dfc8393eff7ae9c5601a0c45\";\nDialog.event_name_to_url[\"BrowserLogin-GoogleAuth-Popup-NotAuthorized\"] = \"https://app.asana.com/app/asana/-/log?se=%7B%22name%22%3A%22BrowserLogin-GoogleAuth-Popup-NotAuthorized%22%2C%22user_agent%22%3A%22Chrome%2F25.0.1364.160%22%7D&cb=1409177825644&hash=cf830e279ad33bc2190b41ad25e8374968655300e4376fbde003bb2ccf31d927\";\nDialog.event_name_to_url[\"BrowserLogin-GoogleAuth-Popup-Authorized\"] = \"https://app.asana.com/app/asana/-/log?se=%7B%22name%22%3A%22BrowserLogin-GoogleAuth-Popup-Authorized%22%2C%22user_agent%22%3A%22Chrome%2F25.0.1364.160%22%7D&cb=1409177825645&hash=b771dc185c05ea808f8b189c1036175ab7c1988928ef6d72f1297170bed46f9c\";</script><div class=\"dialog-page auto-fill-height-0\"><div class=\"formpage-header\"><div class=\"formpage-spacer\"></div></div><div class=\"sidebars\" id=\"sidebars\"><div class=\"dialog-container clearfix\" id=\"dialog-container\"><div class=\"formpage-content\"><div class=\"title\">Log In</div><div class=\"title-bottom\"><span/></div><div class=\"dialog-text\"><table width=\"100%\"><tr><td class=\"content\"><form id=\"dialog_form\" action=\"https://app.asana.com/app/asana/-/login\" method=\"post\"><input type=\"hidden\" name=\"u\" value=\"https://app.asana.com/tasks?project=14790966979027\"/><input type=\"hidden\" name=\"i\" value=\"password\"><input type=\"hidden\" name=\"src\" value=\"login\"/><input type=\"hidden\" name=\"auth\"/><input type=\"hidden\" name=\"xsrf_token\" value=\"0cebe1ed6682122da9fd6a68d061ee6b:1409177825636\"/><div class=\"form-view\"><table><tr id=\"google_auth_row\"><td/><td class=\"buttons\" colspan=\"2\"><div tabindex=0 class=\"asana-button enabled primary button\" id=\"google_auth_button\" onclick=\"return Dialog.Login.loginWithGoogle(false, 'https://app.asana.com/-/oauth2callback', 'https://app.asana.com/tasks?project=14790966979027')\"><span class=\"button-text\">Log In with Google Account</span></div></td></tr><tr><td colspan=\"3\"><div id=\"google_auth_separator\" class=\"separator login-page-separator\"><div class=\"or_separator\">or</div></div></td></tr><tr id=\"email_row\"><td class=\"field-name email\">Email Address</td><td class=\"field-value email\"><input class=\"generic-input showing\" type=\"email\" name=\"e\" id=\"email_input\" value=\"\" onfocus=\"Dialog.setFocusedRow('email_row', true);\" onblur=\"Dialog.setFocusedRow('email_row', false);\"/></td><td class=\"field-no-status\"></td></tr><tr><td colspan=\"2\" class=\"spacer\"/></tr><tr id=\"password_row\"><td class=\"field-name password\">Password</td><td class=\"field-value password\"><input class=\"generic-input showing\" type=\"password\" name=\"p\" id=\"password_input\" onfocus=\"Dialog.setFocusedRow('password_row', true);\" onblur=\"Dialog.setFocusedRow('password_row', false);\"/></td><td class=\"field-no-status\"></td></tr><tr><td></td><td class=\"field-description\">Forgot your password?</td></tr><tr><td></td><td class=\"buttons\" colspan=\"2\"><div tabindex=0 class=\"asana-button enabled primary button\" id=\"submit_button\" onclick=\"Dialog.submit();\"><span class=\"button-text\">Log In</span></div></td></tr></table></div><script>Dialog.makeElementSubmit('email_input');Dialog.makeElementSubmit('password_input'); Dialog.makeElementSubmit('submit_button');</script></form></td></tr></table></div></div><div class=\"trailer\"><div class=\"formpage-footer\"><a class=\"footer-link\" href=\"http://asana.com\" target=\"_blank\">About Asana</a>|<a class=\"footer-link\" href=\"http://asana.com/blog\" target=\"_blank\">Blog</a>|<a class=\"footer-link\" href=\"http://asana.com/jobs\" target=\"_blank\">Jobs</a>|<a class=\"footer-link\" href=\"http://asana.com/help\" target=\"_blank\">Help</a>|<a class=\"footer-link\" href=\"http://asana.com/terms\" target=\"_blank\">Terms</a></div><div class=\"login-link\"><!-- DEBUG-TAG: login-signup -->Dont have an account? <a class=\"footer-link\" href=\"http://www.asana.com/?utm_source=unknown&utm_campaign=app.asana.com\">Sign Up</a></div><!-- Commenting out FBConnect for now.<td class=\"facebook-label\">Or, Connect with Facebook</td><td class=\"facebook-button\"><img src=\"/-/static/luna/browser/images/connect.gif\" alt=\"Facebook Connect\" />--></div></div></div></div></div></div><script>var initial_focused_element = 'email_input';</script><script>\n document.getElementById(initial_focused_element).focus();\n\n</script>\n<div id=\"debug_page_load_marker\" name=\"login\" style=\"display:none\"></div>\n</body>\n</html>"
Faraday interprets /tasks as being an absolute path on the server, so you should see
$ ASANA_URL="https://app.asana.com/api/1.0/" ruby test.rb
I, [2014-08-28T11:18:31.830026 #49279] INFO -- : get https://app.asana.com/tasks?project=14790966979027
Note the URL is wrong - it should be https://app.asana.com/api/1.0/tasks?project=14790966979027. Which is what you get if you replace conn.get "/tasks" => conn.get "tasks" on line 8.
Always good to check the debug output to make sure the URL matches with what you were sending with curl :-)