I've created a c# webservice that allows our front end support teams to view and update a few selected Active Directory values using system.directoryservices
Fields that I want to update are [job] title, department, telephone and employeeid.
I can use a service account with "delegates rights" to update [job] title, department, telephone etc. but when I try to update employeeid I get an "not authorised" error message.
If I use a domain admin account then the same code works fine.
I don't want to use a domain admin account for this webservice, so what privileges do I need?
ANSWER
The ADS_SCHEMA_ID_GUID_USER GUID allows you to update the base user class details, including the employee id
Based on MSDN article
The vbscript used to grant to the service account user the selected delegated rights:
REM #
REM # Delegate AD property set admin rights to named account
REM # Based on: http://www.microsoft.com/technet/scriptcenter/topics/security/propset.mspx
REM #
Const TRUSTEE_ACCOUNT_SAM = "ad\ADStaffUpdates"
Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &H5
Const ADS_RIGHT_DS_READ_PROP = &H10
Const ADS_RIGHT_DS_WRITE_PROP = &H20
Const ADS_FLAG_OBJECT_TYPE_PRESENT = &H1
Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = &H2
Const ADS_ACEFLAG_INHERIT_ACE = &H2
Const ADS_SCHEMA_ID_GUID_USER = "{bf967aba-0de6-11d0-a285-00aa003049e2}"
Const ADS_SCHEMA_ID_GUID_PS_PERSONAL = "{77b5b886-944a-11d1-aebd-0000f80367c1}"
Const ADS_SCHEMA_ID_GUID_PS_PUBLIC = "{e48d0154-bcf8-11d1-8702-00c04fb96050}"
ad_setUserDelegation "OU=USERS, DC=AD, DC=COM", TRUSTEE_ACCOUNT_SAM, ADS_SCHEMA_ID_GUID_PS_USER
ad_setUserDelegation "OU=USERS, DC=AD, DC=COM", TRUSTEE_ACCOUNT_SAM, ADS_SCHEMA_ID_GUID_PS_PERSONAL
ad_setUserDelegation "OU=USERS, DC=AD, DC=COM", TRUSTEE_ACCOUNT_SAM, ADS_SCHEMA_ID_GUID_PS_PUBLIC
Function ad_setUserDelegation( _
ByVal strOU _
,ByVal strTrusteeAccount _
,ByVal strSchema_GUID _
)
Set objSdUtil = GetObject( "LDAP://" & strOU )
Set objSD = objSdUtil.Get( "ntSecurityDescriptor" )
Set objDACL = objSD.DiscretionaryACL
Set objAce = CreateObject( "AccessControlEntry" )
objAce.Trustee = strTrusteeAccount
objAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE
objAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objAce.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT OR ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT
objAce.ObjectType = strSchema_GUID
objACE.InheritedObjectType = ADS_SCHEMA_ID_GUID_USER
objAce.AccessMask = ADS_RIGHT_DS_READ_PROP OR ADS_RIGHT_DS_WRITE_PROP
objDacl.AddAce objAce
objSD.DiscretionaryAcl = objDacl
objSDUtil.Put "ntSecurityDescriptor", Array( objSD )
objSDUtil.SetInfo
End Function
Function ad_revokeUserDelegation( _
ByVal strOU _
,ByVal strTrusteeAccount _
)
Set objSdUtil = GetObject( "LDAP://" & strOU )
Set objSD = objSdUtil.Get( "ntSecurityDescriptor" )
Set objDACL = objSD.DiscretionaryACL
For Each objACE in objDACL
If UCase(objACE.Trustee) = UCase(strTrusteeAccount) Then
objDACL.RemoveAce objACE
End If
Next
objSDUtil.Put "ntSecurityDescriptor", Array(objSD)
objSDUtil.SetInfo
End Function
A sample of the code (the moving parts at least)
string distinguishedname = "CN=Wicks\, Guy,OU=Users,DC=ad,DC=com"
using (DirectoryEntry myDirectoryEntry = new DirectoryEntry(string.Format("LDAP://{0}", distinguishedname), null, null, AuthenticationTypes.Secure))
{
try
{
myDirectoryEntry.Username = "serviceaccount";
myDirectoryEntry.Password = "pa55word";
myDirectoryEntry.Properties["employeeid"][0] = employeeID;
myDirectoryEntry.CommitChanges();
setresult.result = myDirectoryEntry.Properties["employeeid"][0].ToString();
}
catch ( Exception ex )
{
setresult.result = ex.Message;
}
} // end using
(I do apologise for my c#)
do users of your service have the rights to modify those fields through AD users and computers?
if they are then maybe you can use impersonation and just make your service host computer "trusted for delegation" (in AD properties for it) always worked fine for me.
Related
According to the Snowflake docs, when a user executes a copy command it will return 1 of 3 status values:
loaded
load failed
partially loaded
My question is if I use the Python Snowflake Connector (see example code below) to execute a copy command is an exception raised if the status returned is load failed or partially loaded?
Thank you!
copy_dml = 'copy into database.schema.table ' \
'from #fully_qualified_stage pattern = \'.*'+ table_name +'.*[.]json\' ' \
'file_format = (format_name = fully_qualified_json_format) ' \
'force = true;'
try:
import snowflake.connector
#-------------------------------------------------------------------------------------------------------------------------------
#snowflake variables
snowflake_warehouse = credentials.iloc[0]['snowflake_warehouse']
snowflake_account = credentials.iloc[0]['snowflake_account']
snowflake_role = credentials.iloc[0]['snowflake_role']
snowflake_username = credentials.iloc[0]['Username']
snowflake_password = credentials.iloc[0]['Password']
snowflake_connection = ''
cs = ''#snowflake connection cursor
exec_copy_dml = ''
copy_result_field_metadata = ''
copy_result = ''
snowflake_copy_result_df = ''
#-------------------------------------------------------------------------------------------------------------------------------
# load JSON file(s) into Snowflake
snowflake_connection = snowflake.connector.connect(
user = snowflake_username,
password = snowflake_password,
account = snowflake_account,
warehouse = snowflake_warehouse,
role = snowflake_role)
cs = snowflake_connection.cursor()
exec_copy_dml = cs.execute(copy_dml)
copy_result = exec_copy_dml.fetchall()
copy_result_field_metadata = cs.description
snowflake_copy_result_df = snowflake_results_df(copy_result_field_metadata,copy_result)
except snowflake.connector.errors.ProgrammingError as copy_error:
copy_exception_message = "There was a problem loading JSON files to Snowflake," + \
"a snowflake.connector.errors.ProgrammingError exception was raised."
print(copy_exception_message)
raise
except Exception as error_message:
raise
finally:
snowflake_connection.close()
I believe it won't raise exception for load status, you have to check the load status and take necessary action if required.
After you issue your COPY INTO dml, you can run the following query -
SELECT * FROM TABLE(VALIDATE(TABLE_NAME, job_id => '_last'))
This will give you details on the files that you were trying to load. It will normally return empty, unless you encountered issues upload.
You can save this save results in an object and make necessary control adjustments.
Im am using a family account (premium) and this code returns a'Premium required' error. My code is as follows:
device_id = '0d1841b0976bae2a3a310dd74c0f3df354899bc8'
def playSpotify():
client_credentials_manager = SpotifyClientCredentials(client_id='<REDACTED>', client_secret='<REDACTED>')
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
playlists = sp.user_playlists('gh8gflxedxmp4tv2he2gp92ev')
#while playlists:
#for i, playlist in enumerate(playlists['items']):
#print("%4d %s %s" % (i + 1 + playlists['offset'], playlist['uri'], playlist['name']))
#if playlists['next']:
#playlists = sp.next(playlists)
#else:
#playlists = None
#sp.shuffle(true, device_id=device_id)
#sp.repeat(true, device_id=device_id)
sp.start_playback(device_id=device_id, context_uri='spotify:playlist:4ndG2qFEFt1YYcHYt3krjv')
When using SpotifyClientCredentials the token that is generated doesn't belong to any user but to an app, hence the error message.
What you need to do is use SpotifyOAuth instead. So to initialize spotipy, just do:
sp = spotipy.Spotify(auth_manager=spotipy.SpotifyOAuth())
This will open a browser tab and require you to sign in to your account.
I seem to be having an issue with the pro bono tac_plus configuration.
my switch is giving me the following log message
May 4 20:58:52 sv5-c1-r104-ae02 Aaa: %AAA-4-EXEC_AUTHZ_FAILED: User jdambly failed authorization to start a shell
if I look at the tac_plus logs it looks like my group mapping is not configured correctly, here is the log
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: Start authorization request
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: cfg_get: checking user/group jdambly, tag (NULL)
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: cfg_get: checking user/group jdambly, tag (NULL)
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: user 'jdambly' found
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: cfg_get: checking user/group jdambly, tag (NULL)
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: jdambly#192.168.0.19: not found: svcname=shell#world protocol=
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: jdambly#192.168.0.19: not found: svcname=shell protocol=
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: jdambly#192.168.0.19: svcname=shell protocol= not found, default is <unknown>
May 4 14:04:22 neteng tac_plus[14476]: 1/9a920270: Writing AUTHOR/FAIL size=18
here is my config
id = tac_plus {
debug = PACKET AUTHEN AUTHOR MAVIS
access log = /var/log/tac_plus/access.log
accounting log = /var/log/tac_plus/acct.log
authorization log = /var/log/tac_plus/auth.log
mavis module = external {
setenv LDAP_SERVER_TYPE = "microsoft"
#setenv LDAP_HOSTS = "ldaps://xxxxxx:3268"
setenv LDAP_HOSTS = "xxxxxx:3268"
setenv LDAP_SCOPE = sub
setenv LDAP_BASE = "dc=nskope,dc=net"
setenv LDAP_FILTER = "(&(objectclass=user)(sAMAccountName=%s))"
setenv LDAP_USER = "xxxx#nskope.net"
setenv LDAP_PASSWD = "xxxxxxxx"
#setenv AD_GROUP_PREFIX = devops
# setenv REQUIRE_AD_GROUP_PREFIX = 1
# setenv USE_TLS = 0
exec = /usr/local/lib/mavis/mavis_tacplus_ldap.pl
}
user backend = mavis
login backend = mavis
pap backend = mavis
skip missing groups = yes
host = world {
address = 0.0.0/0
prompt = "Welcome\n"
key = cisco
}
group = devops {
default service = permit
service = shell {
default command = permit
default attribute = permit
set priv-lvl = 15
}
}
}
I'm trying to map the ad group devops to the group in the config but I think that's failing and I don't get why
so LONG story short I got this working using the following config.
#!../../../sbin/tac_plus
id = spawnd {
listen = { port = 49 }
spawn = {
instances min = 1
instances max = 10
}
background = no
}
id = tac_plus {
debug = PACKET AUTHEN AUTHOR MAVIS
access log = /var/log/tac_plus/access.log
accounting log = /var/log/tac_plus/acct.log
authorization log = /var/log/tac_plus/auth.log
mavis module = external {
setenv LDAP_SERVER_TYPE = "microsoft"
#setenv LDAP_HOSTS = "ldaps://xxxxxxxxx:3268"
setenv LDAP_HOSTS = "xxxxxxxxx:3268"
#setenv LDAP_SCOPE = sub
setenv LDAP_BASE = "cn=Users,dc=nskope,dc=net"
setenv LDAP_FILTER = "(&(objectclass=user)(sAMAccountName=%s))"
setenv LDAP_USER = "xxxxxxxx"
setenv LDAP_PASSWD = "xxxxxxxx"
#setenv FLAG_FALLTHROUGH=1
setenv UNLIMIT_AD_GROUP_MEMBERSHIP = "1"
#setenv EXPAND_AD_GROUP_MEMBERSHIP=1
#setenv FLAG_USE_MEMBEROF = 1
setenv AD_GROUP_PREFIX = ""
# setenv REQUIRE_AD_GROUP_PREFIX = 1
# setenv USE_TLS = 0
exec = /usr/local/lib/mavis/mavis_tacplus_ldap.pl
}
user backend = mavis
login backend = mavis
pap backend = mavis
skip missing groups = yes
host = world {
address = 0.0.0/0
#prompt = "Welcome\n"
key = cisco
}
group = devops {
default service = permit
service = shell {
default command = permit
default attribute = permit
set priv-lvl = 15
}
}
}
what really did the trick is adding
setenv UNLIMIT_AD_GROUP_MEMBERSHIP = "1"
setenv AD_GROUP_PREFIX = ""
with these settings it's not looking for a prefix to the all the ad groups. This config allows for a direct mappings of ad group to the group configured in this file, in my case the group is called dev ops. also note that I had to use quotes around the 1. without these quests it does not set the var UNLIMIT_AD_GROUP_MEMBERSHIP to one so watch out for that. hopefully this can help someone else so they do not have to go through all the pain I did ;)
i've a very specific problem, but i'm a niewbie with zend framework so i don't have idea of how exctly this db adapter works as a configuration, but i've already made a db connection with the default adapter of zend, and it was successful. Now i've to set two different database connections for two different db in the same application. So i've taken my application.ini and i've written the following lines:
;connessione al db
resources.db.adapter = pdo_mssql
resources.db.params.host = "ip"
resources.db.params.username = user
resources.db.params.password = pwd
resources.db.params.dbname = NAME
resources.db.isDefaultTableAdapter = true
resources.db.params.pdoType = dblib
;connessione al db1
resources.db1.adapter = pdo_mssql
resources.db1.params.host = "ip"
resources.db1.params.username = user
resources.db1.params.password = pwd
resources.db1.params.dbname = NAME
resources.db1.isDefaultTableAdapter = false
resources.db1.params.pdoType = dblib
then i went to my action controller and i wrote:
$db = Zend_Registry::get ( 'db' );
$result = $db->fetchRow("SELECT [Sell-to Customer No_] FROM dbo.SyncroPlanningTable WHERE id='".$id);
$rag_soc=$result->{"Sell-to Customer No_"};
$db1 = Zend_Registry::get ( 'db1' );
$result1 = $db1->fetchRow("SELECT [No_],[Name],[Address],[City],[Contact],[Name],[Phone] FROM `dbo.SOS$Customer` WHERE No_ = '".$rag_soc."'");
The error i'm getting is the following:
Fatal error: Uncaught exception 'Zend_Application_Bootstrap_Exception' with message 'Unable to resolve plugin "db1";
UPDATE:
My bootstrap.php is:
$resource = $this->getPluginResource ( "db" );
$db = $resource->getDbAdapter ();
$db->setFetchMode ( Zend_Db::FETCH_OBJ );
Zend_Db_Table_Abstract::setDefaultAdapter ( $db );
Zend_Registry::set ( "db", $db );
How can i change it? it is not mentioned in the manual page you gave me.
resources.db refers to Zend_Application_Resource_Db, so "db" here is not a variable name.
You should use Zend_Application_Resource_Multidb to support multiple database connections:
http://framework.zend.com/manual/1.12/en/zend.application.available-resources.html#zend.application.available-resources.multidb
Your code is expecting the DB adapters to be in the registry, so you need to grab them from the multiDB resource and store them:
$multiDB = $this->getPluginResource('multidb');
Zend_Registry::set('db1', $multiDB->getDb('db1');
Zend_Registry::set('db2', $multiDB->getDb('db2');
also, this line:
Zend_Db_Table_Abstract::setDefaultAdapter ( $db );
can be removed, as you're specifying the default adapter in the application.ini.
I'm trying to synchronize OpenLDAP and Active directory together. To do so I'm using a program called LSC-Project which is specified to do this sort of thing.
I have configured the program the best I can however I can't find a way to shake off the following error:
javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-
031001CD,
problem 2001 (NO_OBJECT), data 0, best match of:
'DC=domname,DC=com'
]; remaining name
'uid=user1,ou=Users'
May 09 15:19:25 - ERROR - Error while synchronizing ID uid=user1,ou=Users:
java.lang.Exception:
Technical problem while applying modifications to directory
dn: uid=user1,ou=Users,dc=domname,dc=com
changetype: add
userPassword: 3+kU2th/WMo/v553A24a3SBw2kU=
objectClass: uid
This is the configuration file that the program runs on:
###############################
Destination LDAP directory #
##############################
dst.java.naming.provider.url = ldap://192.168.1.3:389/dc=Windows,dc=com
dst.java.naming.security.authentication = simple
dst.java.naming.security.principal = cn=Administrator,cn=Users,dc=Windows,dc=com
dst.java.naming.security.credentials = 11111
dst.java.naming.referral = ignore
dst.java.naming.ldap.derefAliases = never
dst.java.naming.factory.initial = com.sun.jndi.ldap.LdapCtxFactory
dst.java.naming.ldap.version = 3
dst.java.naming.ldap.pageSize = 1000
#########################
Source LDAP directory
#########################
src.java.naming.provider.url = ldap://192.168.1.2:389/dc=Linux,dc=com
src.java.naming.security.authentication = simple
src.java.naming.security.principal = uid=root,ou=users,dc=Linux,dc=com
src.java.naming.security.credentials = 11111
src.java.naming.referral = ignore
src.java.naming.ldap.derefAliases = never
src.java.naming.factory.initial = com.sun.jndi.ldap.LdapCtxFactory
src.java.naming.ldap.version = 3
#######################
Tasks configuration
#######################
lsc.tasks = Administrator
lsc.tasks.Administrator.srcService = org.lsc.jndi.SimpleJndiSrcService
lsc.tasks.Administrator.srcService.baseDn = ou=users
lsc.tasks.Administrator.srcService.filterAll = (&(objectClass=person))
lsc.tasks.Administrator.srcService.pivotAttrs = uid
lsc.tasks.Administrator.srcService.filterId = (&(objectClass=person)(uid={uid}))
lsc.tasks.Administrator.srcService.attrs = description uid userPassword
lsc.tasks.Administrator.dstService = org.lsc.jndi.SimpleJndiDstService
lsc.tasks.Administrator.dstService.baseDn = cn=Users
lsc.tasks.Administrator.dstService.filterAll = (&(cn=*)(objectClass=organizationalPerson))
lsc.tasks.Administrator.dstService.pivotAttrs = cn, top, person, user, organizationalPerson
lsc.tasks.Administrator.dstService.filterId = (&(objectClass=user) (sAMAccountName={cn}))
lsc.tasks.Administrator.dstService.attrs = description cn userPassword objectClass
lsc.tasks.Administrator.bean = org.lsc.beans.SimpleBean
lsc.tasks.Administrator.dn = "uid=" + srcBean.getAttributeValueById("uid") + ",ou=Users"
dn.real_root = dc=Domname,dc=com
#############################
Syncoptions configuration
#############################
lsc.syncoptions.Administrator = org.lsc.beans.syncoptions.PropertiesBasedSyncOptions
lsc.syncoptions.Administrator.default.action = M
lsc.syncoptions.Administrator.objectClass.action = M
lsc.syncoptions.Administrator.objectClass.force_value = srcBean.getAttributeValueById("cn").toUpperCase()
lsc.syncoptions.Administrator.userPassword.default_value = SecurityUtils.hash(SecurityUtils.HASH_SHA1, "defaultPassword")
lsc.syncoptions.Administrator.default.delimiter=;
lsc.syncoptions.Administrator.objectClass.force_value = "top";"user";"person";"organizationalPerson"
lsc.syncoptions.Administrator.userPrincipalName.force_value = srcBean.getAttributeValueById("uid") + "#Domname.com"
lsc.syncoptions.Administrator.userAccountControl.create_value = AD.userAccountControlSet ( "0", [AD.UAC_SET_NORMAL_ACCOUNT])
I'm suspecting that it has something to do with the baseDn of the Task configuration in the part of the source configuration.
The OSs is ubuntu 10.04 and Windows2K3
Someone suggested to me to make a manual sync between them but I have not found any guides to do so. And this program is pretty much the only thing that says that is does this kind of job without costs.
The baseDn should be the distinguished name of the base object of the search, for example, ou=users,dc=domname,dc=com.
see also
LDAP: Mastering Search Filters
LDAP: Search best practices
LDAP: Programming practices
The main reason for NameNotFoundException is that the object which you're searching doesn't exist or the container in which you are searching is not correct.
In case of Spring-ldap, we used to get this error when we specify the baseDn in the context file(LdapContextSource bean) and also in createUser code to build userDn.we need not specify the dc again in the buildUserDn()
protected Name buildUserDn(String userName) {
DistinguishedName dn = new DistinguishedName();
//only cn is required as the base dn is already specified in context file
dn.add("cn", userName);
return dn;
}
In Active Directory: Users catalog is container class, not OrganizationalUnit, so you should use: cn=users,dc=domname,dc=com