GitLab Change LDAP bind username structure - ruby

I would like to change how Gitlab verifies authentication with the AD, Since it sends the request as "CN=user ou=xx dc=xx". But the AD needs it to be sent as Domain\user. How can I change Gitlab config to send "domain\username' in bind request ?
Or why would the Windows AD reject the authentication?
Below is my LDAP configuration
gitlab_rails['ldap_servers'] = {
'main' => {
'label' => 'AD',
'host' => '10.0.0.1',
'port' => 389,
'uid' => 'sAMAccountName',
'base' => 'DC=AAA,DC=ORG,DC=LOCAL',
'bind_dn' => 'AAA\abcdefgh',
'password' => 'Password4',
'block_auto_created_users'=> 'true',
'active_directory' => true,
'lowercase_usernames' => true,
}
}
The wireshark image is below.
Bind Password sent packet 4.
Bind Password sent packet 18

Active Directory doesn't need the logon to be in the format domain\uid -- that's one of the three valid ID formats you can use when binding to AD via LDAP, but uid#domain and LDAP fully qualified DN are equally valid. What GitLab does is binds to AD using the bind_dn and password in the config (wireshark #4), searches at the base configured as 'base' for (&(sAMAccountName=*user supplied uid*)) (wireshark #11), returns the fully qualified DN of the identified account (wireshark #16), and then validates the user's credentials by binding with the fully qualified DN and user-supplied password (wireshark #18).
52e (in the bind error data) is returned when the user ID matches a valid user but the password is incorrect. Is the correct user being found for the ID supplied (i.e. the user is in the dc=aaa,dc=org,dc=local domain, and can be found under Head Office\Users OU)? If you select packet 18, you'll see the password within the packet data -- verify something didn't get mangled in transit or mistyped.

Related

OctoKit Ruby Authentication

I'm sure that this is a simple error, but I'm interested in writing a program that collects information on all of my github repositories. While this seems simple enough to do with Octokit, I've run into issues associated with authenticating my session.
client = Octokit::Client.new \
:login => 'MY_USER_NAME',
:password => 'MY_PASSWORD'
puts client
user = client.user("MY_USER_NAME", :headers => { "PERSONAL_ACCESS_TOKEN_NAME" => "TOKEN" })
puts user
Unfortunately this results in the following:
GET https://api.github.com/users/mccoleman75225: 401 - Must specify two-factor authentication OTP code. // See: https://developer.github.com/v3/auth#working-with-two-factor-authentication (Octokit::OneTimePasswordRequired)
How does someone go about authenticating their session?
As of January 2022, you can create a PAT (Personal Access Token) in your GitHub Developer Settings and use that to connect through the Octokit client like so:
client = Octokit::Client.new(:access_token => "<Your Personal Access Token>")
user = client.user
user.login
# => "monacat"
Here's a step-by-step guide on how to create a PAT. Try to select the correct permissions when creating your token or you'll get back a 403 error with a message explaining the missing scope. You can always go back and edit your scopes later though.
Sources:
Octokit.rb — Authentication
GitHub API Authentication - Personal Access Tokens
Looks like you have 2 Factor Authentication enabled on your account so you'll need to add your 2FA token:
client = Octokit::Client.new \
:login => 'defunkt',
:password => 'c0d3b4ssssss!'
client.create_authorization(:scopes => ["user"], :note => "Name of token",
:headers => { "X-GitHub-OTP" => "<your 2FA token>" })
# => <your new oauth token>
See documentation

Specify elasticsearch username/password with Elasticsearch-PHP ClientBuilder

I have activated default "elastic" user and set a password for that user. I am using elasticsearch-php to connect and query my elasticsearch. It was working good, but after activating the password I cannot connect using my previous code.
While looking for providing authentication information with the ClientBuilder, I only get configurations regarding ssh connection. Did not find anything how can I use my elasticsearch username and password with the connection.
$hosts = ['127.0.0.1:9200'];
$es_user = 'elastic';
$es_pass = 'MY_PASS';
$elasticsearch = Elasticsearch\ClientBuilder::create()->setHosts($hosts)->build();
I am wondering how can I use my username/password in the connection above.
You can do it with an extended host configuration like this:
$hosts = [
[
'host' => '127.0.0.1',
'port' => '9200',
'user' => 'elastic',
'pass' => 'MY_PASS'
]
];
$elasticsearch = Elasticsearch\ClientBuilder::create()
->setHosts($hosts)
->build();

PayPal Adaptive Payments - How do I get an account ID?

I'm trying to use the PayPal Adaptive Payments Payment API. I am trying to test the API with my own email address, but I get an error specifying the sender by email. I'm wondering how to determine my own account ID to try that way?
I'm referring to the account id here: https://developer.paypal.com/docs/classic/api/adaptive-payments/PaymentDetails_API_Operation/
I get the error: "The email address smithd98#gmail.com is invalid. It may not be registered in PayPal's system yet" when trying to pay by my email. When I log in with PayPal my email address is correct. My account is verified with a bank account, credit card, and debit card connected.
Here is my code:
require 'paypal-sdk-adaptivepayments'
PayPal::SDK.configure(
:mode => "sandbox", # Set "live" for production
:app_id => "HIDDEN",
:username => "HIDDEN",
:password => "HIDDEN",
:signature => "HIDDEN" )
#api = PayPal::SDK::AdaptivePayments.new
# Build request object
#pay = #api.build_pay({
:actionType => "PAY",
:cancelUrl => "http://localhost:3000/samples/adaptive_payments/pay",
:currencyCode => "USD",
:feesPayer => "SENDER",
:ipnNotificationUrl => "http://localhost:3000/samples/adaptive_payments/ipn_notify",
:receiverList => {
:receiver => [{
:amount => 1.0,
:email => "smithd98#gmail.com" }] },
:returnUrl => "http://localhost:3000/samples/adaptive_payments/pay" })
##pay.sender.email = "smithd98#gmail.com"
#pay.sender.accountId = 23434
# Make API call & get response
puts #pay.inspect
#response = #api.pay(#pay)
# Access response
if #response.success?
puts 'responsepayKey'
#response.payKey
puts 'url to complete payment'
#api.payment_url(#response) # Url to complete payment
else
puts 'error'
puts #response.error[0].message
puts #response.error[1].parameter.value
end
You can get it via the API using GetPalDetails. You can get it manually in your PayPal profile. Go to the Business Info section and you'll see a Merchant Account ID.
On another note, I see that you're using localhost in your return and cancel URL's, as well as IPN. This isn't going to work. Remember, it's PayPal's server that will be hitting that address. If they hit localhost, they're just hitting their own server, which isn't going to do what you want, of course.
If you want to test all of this with your local test server you'll need to use your IP address or setup some DNS and a domain of some sort. I personally like to follow PayPal's lead and setup a sandbox.domain.com as a test server for any site I'm working on.
David,
To get the Account ID of your test accounts.
Login into your sandbox paypal account at https://www.sandbox.paypal.com/
Click on the "Profile" tab on the top menu
You will see your Account ID for that account beside "Merchant account ID"
It's the same exact process for a real account but instead login into your real paypal account.

Trouble with Google Apps API and Service Accounts in Ruby

I'm having some trouble getting the sample code for instantiating a Drive Service Account working. I've set up the service account in the API console as directed and included the scope for the 'https://www.googleapis.com/auth/drive', but running this generates the following error: "Authorization failed. Server message: (Signet::AuthorizationError)".
Oddly, if I omit the user_email address it doesn't generate an error.
My objective is to be able to do an audit on all the files stored on the organization's Drive, and it's my understanding that using a service account would be the way to get a listing of all the files stored.
Have I missed some special setting on the server side for this?
require 'google/api_client'
## Email of the Service Account #
SERVICE_ACCOUNT_EMAIL = '<service account email>#developer.gserviceaccount.com'
## Path to the Service Account's Private Key file #
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '<private key file>-privatekey.p12'
def build_client(user_email)
key = Google::APIClient::PKCS12.load_key(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'notasecret')
asserter = Google::APIClient::JWTAsserter.new(SERVICE_ACCOUNT_EMAIL, 'https://www.googleapis.com/auth/drive', key)
client = Google::APIClient.new
client.authorization = asserter.authorize(user_email)
return client
end
client = build_client("<users email address>")
This looks to me like you are using an older example. I think that's how you used to do it about a year ago. Back in late 2012 that method of setting up the app was deprecated because Signet was updated to handle all aspects of the OAuth2 setup.
Here is the code I generally use to create a service account. You can tweak it to fit into your method.
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => "https://www.googleapis.com/auth/drive",
:issuer => "<service account email>#developer.gserviceaccount.com",
:signing_key => Google::APIClient::KeyUtils.load_from_pkcs12("<private key file>-privatekey.p12", "notasecret"),
:person => "<users email address>")
client.authorization.fetch_access_token!
If you are still having issues let me know and I'll see if I can help.
Using version 0.9.13 of google-api-client, I succeeded in using the following slight adaptation of Woodward's answer (note the absence of the person parameter):
def service_account_authorization(credentials_file, scope)
credentials = JSON.parse(File.open(credentials_file, 'rb').read)
authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => scope,
:issuer => credentials['client_id'],
:signing_key => OpenSSL::PKey::RSA.new(credentials['private_key'], nil),
)
authorization.fetch_access_token!
authorization
end
This snippet takes a file as it was downloaded from Google Cloud Console for a service account and returns an auth object that can be fed to Google::Apis::*Service.authorization.
Thanks James!
I have worked with service account+Drive+file permissions using Java. In order to use permissions for a particular user, I had to allow certain scope. The only thing I can guess about your issue is that you might have missed the Delegation part

Grape API and HTTP Digest Authentication

I am working on creating an API for my ruby application that authenticates users based on HTTP Digest Authentication. I decided to use the Grape API library because it makes creating an API cleaner in ruby. The Grape documentation states that you can use Digest Authentication like:
http_digest({ :realm => 'Test Api', :opaque => 'app secret' }) do |username|
# lookup the user's password here
{ 'user1' => 'password1' }[username]
end
The Grape implementation above is a wrapper for Rack::Auth::Digest::MD5
Now also for security i read that as of RFC 2617 you don't need to store the password as plain text in the database you store an MD5 digest of the username:realm:password and authticate against that so i created a DataMapper model:
class Key
include DataMapper::Resource
property :id, Serial
property :username, String
property :password, String
property :active, Boolean, :default => true
property :created_at, DateTime, :default => DateTime.now
property :updated_at, DateTime
end
Now with what I provided, I am lost as to how to connect these two and make it work.
Unfortunately, Rack::Auth::Digest::MD5 expects a plaintext password on the server side.
The Grape example code shows a hard-coded lookup of password.
You could replace { 'user1' => 'password1' }[username] with
Key.first( :username => username ).password
provided you stored plaintext passwords in the Key class. You could store these reversibly-encrypted I suppose, although that doesn't add much security unless you construct relatively complex/costly schemes for key management.
Not sure if there is a way around this that would let you store hashed passwords. MD5 isn't the most secure hashing choice (although better than nothing!). If security is an important concern for your API, you will want to look beyond digest auth - using https would help, for example.
Edit: Following a bit of to-and-fro in discussions, the following variation of Grape's example does allow you to store the MD5'd password:
auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
Digest::MD5.hexdigest( 'fred:Llama:654321' )
end
The example gives a hard-coded username:'fred', password:'654321' response. So I think your target code is something like:
auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
k = Key.first( :username => username )
k ? k.password : nil
end
And you store the result of Digest::MD5.hexdigest( "#{username}:#{realm}:#{password}" ) in each user's password property.
Note the double-level hash with :realm twice. This is a bit hacky, but at least you don't have to write your own middleware, Grape is still dealing with it. This is not a documented feature of Grape or covered with tests, so may not work in future versions.

Resources