mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merge branch 'upstream'
This commit is contained in:
commit
767c009d48
14
ACI.txt
14
ACI.txt
@ -50,6 +50,8 @@ dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "member")(targetfilter = "(&(!(cn=admins))(objectclass=ipausergroup))")(version 3.0;acl "permission:System: Modify Group Membership";allow (write) groupdn = "ldap:///cn=System: Modify Group Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "member")(targetfilter = "(&(!(cn=admins))(objectclass=ipausergroup))")(version 3.0;acl "permission:System: Modify Group Membership";allow (write) groupdn = "ldap:///cn=System: Modify Group Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || description || gidnumber || ipauniqueid || mepmanagedby || objectclass")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Modify Groups";allow (write) groupdn = "ldap:///cn=System: Modify Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "cn || description || gidnumber || ipauniqueid || mepmanagedby || objectclass")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Modify Groups";allow (write) groupdn = "ldap:///cn=System: Modify Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "cn || gidnumber || memberuid || objectclass")(target = "ldap:///cn=groups,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Group Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "member || memberhost || memberof || memberuid || memberuser")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Read Group Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "member || memberhost || memberof || memberuid || memberuser")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Read Group Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
dn: cn=groups,cn=accounts,dc=ipa,dc=example
|
||||||
@ -96,6 +98,8 @@ dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "ipasshpubkey")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host SSH Public Keys";allow (write) groupdn = "ldap:///cn=System: Manage Host SSH Public Keys,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "ipasshpubkey")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host SSH Public Keys";allow (write) groupdn = "ldap:///cn=System: Manage Host SSH Public Keys,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "description || l || macaddress || nshardwareplatform || nshostlocation || nsosversion || userclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Modify Hosts";allow (write) groupdn = "ldap:///cn=System: Modify Hosts,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "description || l || macaddress || nshardwareplatform || nshostlocation || nsosversion || userclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Modify Hosts";allow (write) groupdn = "ldap:///cn=System: Modify Hosts,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "cn || macaddress || objectclass")(target = "ldap:///cn=computers,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Host Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "memberof")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Read Host Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "memberof")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Read Host Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||||
@ -126,6 +130,8 @@ dn: cn=ng,cn=alt,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "externalhost || member || memberhost || memberuser")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Modify Netgroup Membership";allow (write) groupdn = "ldap:///cn=System: Modify Netgroup Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "externalhost || member || memberhost || memberuser")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Modify Netgroup Membership";allow (write) groupdn = "ldap:///cn=System: Modify Netgroup Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
||||||
aci: (targetattr = "description")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Modify Netgroups";allow (write) groupdn = "ldap:///cn=System: Modify Netgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "description")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Modify Netgroups";allow (write) groupdn = "ldap:///cn=System: Modify Netgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "cn || membernisnetgroup || nisnetgrouptriple || objectclass")(target = "ldap:///cn=ng,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Netgroup Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
||||||
aci: (targetattr = "externalhost || member || memberhost || memberof || memberuser || objectclass")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Read Netgroup Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "externalhost || member || memberhost || memberof || memberuser || objectclass")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Read Netgroup Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
dn: cn=ng,cn=alt,dc=ipa,dc=example
|
||||||
@ -213,7 +219,7 @@ aci: (targetattr = "cmdcategory || description || externalhost || externaluser |
|
|||||||
dn: cn=sudorules,cn=sudo,dc=ipa,dc=example
|
dn: cn=sudorules,cn=sudo,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cmdcategory || cn || description || externalhost || externaluser || hostcategory || hostmask || ipaenabledflag || ipasudoopt || ipasudorunas || ipasudorunasextgroup || ipasudorunasextuser || ipasudorunasextusergroup || ipasudorunasgroup || ipasudorunasgroupcategory || ipasudorunasusercategory || ipauniqueid || member || memberallowcmd || memberdenycmd || memberhost || memberuser || objectclass || sudonotafter || sudonotbefore || sudoorder || usercategory")(targetfilter = "(objectclass=ipasudorule)")(version 3.0;acl "permission:System: Read Sudo Rules";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cmdcategory || cn || description || externalhost || externaluser || hostcategory || hostmask || ipaenabledflag || ipasudoopt || ipasudorunas || ipasudorunasextgroup || ipasudorunasextuser || ipasudorunasextusergroup || ipasudorunasgroup || ipasudorunasgroupcategory || ipasudorunasusercategory || ipauniqueid || member || memberallowcmd || memberdenycmd || memberhost || memberuser || objectclass || sudonotafter || sudonotbefore || sudoorder || usercategory")(targetfilter = "(objectclass=ipasudorule)")(version 3.0;acl "permission:System: Read Sudo Rules";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: dc=ipa,dc=example
|
dn: dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || description || objectclass || ou || sudocommand || sudohost || sudonotafter || sudonotbefore || sudooption || sudoorder || sudorunas || sudorunasgroup || sudorunasuser || sudouser")(target = "ldap:///ou=sudoers,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Sudoers compat tree";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || description || objectclass || ou || sudocommand || sudohost || sudonotafter || sudonotbefore || sudooption || sudoorder || sudorunas || sudorunasgroup || sudorunasuser || sudouser")(target = "ldap:///ou=sudoers,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Sudoers compat tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=trusts,dc=ipa,dc=example
|
dn: cn=trusts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || ipantflatname || ipantsecurityidentifier || ipantsidblacklistincoming || ipantsidblacklistoutgoing || ipanttrusteddomainsid || ipanttrustpartner || objectclass")(version 3.0;acl "permission:System: Read Trust Information";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || ipantflatname || ipantsecurityidentifier || ipantsidblacklistincoming || ipantsidblacklistoutgoing || ipanttrusteddomainsid || ipanttrustpartner || objectclass")(version 3.0;acl "permission:System: Read Trust Information";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=trusts,dc=ipa,dc=example
|
dn: cn=trusts,dc=ipa,dc=example
|
||||||
@ -232,6 +238,8 @@ dn: cn=UPG Definition,cn=Definitions,cn=Managed Entries,cn=etc,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "*")(target = "ldap:///cn=UPG Definition,cn=Definitions,cn=Managed Entries,cn=etc,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read UPG Definition";allow (compare,read,search) groupdn = "ldap:///cn=System: Read UPG Definition,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "*")(target = "ldap:///cn=UPG Definition,cn=Definitions,cn=Managed Entries,cn=etc,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read UPG Definition";allow (compare,read,search) groupdn = "ldap:///cn=System: Read UPG Definition,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "audio || businesscategory || carlicense || departmentnumber || destinationindicator || employeenumber || employeetype || fax || homephone || homepostaladdress || inetuserhttpurl || inetuserstatus || internationalisdnnumber || jpegphoto || l || labeleduri || mail || mobile || o || ou || pager || photo || physicaldeliveryofficename || postaladdress || postalcode || postofficebox || preferreddeliverymethod || preferredlanguage || registeredaddress || roomnumber || secretary || seealso || st || street || telephonenumber || teletexterminalidentifier || telexnumber || usercertificate || usersmimecertificate || x121address || x500uniqueidentifier")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Addressbook Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "audio || businesscategory || carlicense || departmentnumber || destinationindicator || employeenumber || employeetype || fax || homephone || homepostaladdress || inetuserhttpurl || inetuserstatus || internationalisdnnumber || jpegphoto || l || labeleduri || mail || mobile || o || ou || pager || photo || physicaldeliveryofficename || postaladdress || postalcode || postofficebox || preferreddeliverymethod || preferredlanguage || registeredaddress || roomnumber || secretary || seealso || st || street || telephonenumber || teletexterminalidentifier || telexnumber || usercertificate || usersmimecertificate || x121address || x500uniqueidentifier")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Addressbook Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "cn || gecos || gidnumber || homedirectory || loginshell || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "ipasshpubkey || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "ipasshpubkey || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
@ -252,6 +260,8 @@ dn: cn=CAcert,cn=ipa,cn=etc,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "authorityrevocationlist || cacertificate || certificaterevocationlist || cn || crosscertificatepair || objectclass")(targetfilter = "(objectclass=pkica)")(version 3.0;acl "permission:System: Read CA Certificate";allow (compare,read,search) userdn = "ldap:///anyone";)
|
aci: (targetattr = "authorityrevocationlist || cacertificate || certificaterevocationlist || cn || crosscertificatepair || objectclass")(targetfilter = "(objectclass=pkica)")(version 3.0;acl "permission:System: Read CA Certificate";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=ca_renewal,cn=ipa,cn=etc,dc=ipa,dc=example
|
dn: cn=ca_renewal,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || objectclass || usercertificate")(targetfilter = "(objectclass=pkiuser)")(version 3.0;acl "permission:System: Read CA Renewal Information";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || objectclass || usercertificate")(targetfilter = "(objectclass=pkiuser)")(version 3.0;acl "permission:System: Read CA Renewal Information";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "creatorsname || modifiersname")(targetfilter = "(objectclass=*)")(version 3.0;acl "permission:System: Read Creator and Modifier Operational Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=dna,cn=ipa,cn=etc,dc=ipa,dc=example
|
dn: cn=dna,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || dnahostname || dnaportnum || dnaremainingvalues || dnaremotebindmethod || dnaremoteconnprotocol || dnasecureportnum || objectclass")(targetfilter = "(objectclass=dnasharedconfig)")(version 3.0;acl "permission:System: Read DNA Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || dnahostname || dnaportnum || dnaremainingvalues || dnaremotebindmethod || dnaremoteconnprotocol || dnasecureportnum || objectclass")(targetfilter = "(objectclass=dnasharedconfig)")(version 3.0;acl "permission:System: Read DNA Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=masters,cn=ipa,cn=etc,dc=ipa,dc=example
|
dn: cn=masters,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||||
@ -260,3 +270,5 @@ dn: cn=config
|
|||||||
aci: (targetattr = "cn || description || nsds50ruv || nsds5beginreplicarefresh || nsds5debugreplicatimeout || nsds5flags || nsds5replicaabortcleanruv || nsds5replicaautoreferral || nsds5replicabackoffmax || nsds5replicabackoffmin || nsds5replicabinddn || nsds5replicabindmethod || nsds5replicabusywaittime || nsds5replicachangecount || nsds5replicachangessentsincestartup || nsds5replicacleanruv || nsds5replicacleanruvnotified || nsds5replicacredentials || nsds5replicaenabled || nsds5replicahost || nsds5replicaid || nsds5replicalastinitend || nsds5replicalastinitstart || nsds5replicalastinitstatus || nsds5replicalastupdateend || nsds5replicalastupdatestart || nsds5replicalastupdatestatus || nsds5replicalegacyconsumer || nsds5replicaname || nsds5replicaport || nsds5replicaprotocoltimeout || nsds5replicapurgedelay || nsds5replicareferral || nsds5replicaroot || nsds5replicasessionpausetime || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || nsds5replicatimeout || nsds5replicatombstonepurgeinterval || nsds5replicatransportinfo || nsds5replicatype || nsds5replicaupdateinprogress || nsds5replicaupdateschedule || nsds5task || nsds7directoryreplicasubtree || nsds7dirsynccookie || nsds7newwingroupsyncenabled || nsds7newwinusersyncenabled || nsds7windowsdomain || nsds7windowsreplicasubtree || nsruvreplicalastmodified || nsstate || objectclass || onewaysync || winsyncdirectoryfilter || winsyncinterval || winsyncmoveaction || winsyncsubtreepair || winsyncwindowsfilter")(targetfilter = "(|(objectclass=nsds5Replica)(objectclass=nsds5replicationagreement)(objectclass=nsDSWindowsReplicationAgreement)(objectClass=nsMappingTree))")(version 3.0;acl "permission:System: Read Replication Agreements";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Replication Agreements,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "cn || description || nsds50ruv || nsds5beginreplicarefresh || nsds5debugreplicatimeout || nsds5flags || nsds5replicaabortcleanruv || nsds5replicaautoreferral || nsds5replicabackoffmax || nsds5replicabackoffmin || nsds5replicabinddn || nsds5replicabindmethod || nsds5replicabusywaittime || nsds5replicachangecount || nsds5replicachangessentsincestartup || nsds5replicacleanruv || nsds5replicacleanruvnotified || nsds5replicacredentials || nsds5replicaenabled || nsds5replicahost || nsds5replicaid || nsds5replicalastinitend || nsds5replicalastinitstart || nsds5replicalastinitstatus || nsds5replicalastupdateend || nsds5replicalastupdatestart || nsds5replicalastupdatestatus || nsds5replicalegacyconsumer || nsds5replicaname || nsds5replicaport || nsds5replicaprotocoltimeout || nsds5replicapurgedelay || nsds5replicareferral || nsds5replicaroot || nsds5replicasessionpausetime || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || nsds5replicatimeout || nsds5replicatombstonepurgeinterval || nsds5replicatransportinfo || nsds5replicatype || nsds5replicaupdateinprogress || nsds5replicaupdateschedule || nsds5task || nsds7directoryreplicasubtree || nsds7dirsynccookie || nsds7newwingroupsyncenabled || nsds7newwinusersyncenabled || nsds7windowsdomain || nsds7windowsreplicasubtree || nsruvreplicalastmodified || nsstate || objectclass || onewaysync || winsyncdirectoryfilter || winsyncinterval || winsyncmoveaction || winsyncsubtreepair || winsyncwindowsfilter")(targetfilter = "(|(objectclass=nsds5Replica)(objectclass=nsds5replicationagreement)(objectclass=nsDSWindowsReplicationAgreement)(objectClass=nsMappingTree))")(version 3.0;acl "permission:System: Read Replication Agreements";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Replication Agreements,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=replication,cn=etc,dc=ipa,dc=example
|
dn: cn=replication,cn=etc,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || nsds5flags || nsds5replicaabortcleanruv || nsds5replicaautoreferral || nsds5replicabackoffmax || nsds5replicabackoffmin || nsds5replicabinddn || nsds5replicachangecount || nsds5replicacleanruv || nsds5replicaid || nsds5replicalegacyconsumer || nsds5replicaname || nsds5replicaprotocoltimeout || nsds5replicapurgedelay || nsds5replicareferral || nsds5replicaroot || nsds5replicatombstonepurgeinterval || nsds5replicatype || nsds5task || nsstate || objectclass")(targetfilter = "(objectclass=nsds5replica)")(version 3.0;acl "permission:System: Read Replication Information";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || nsds5flags || nsds5replicaabortcleanruv || nsds5replicaautoreferral || nsds5replicabackoffmax || nsds5replicabackoffmin || nsds5replicabinddn || nsds5replicachangecount || nsds5replicacleanruv || nsds5replicaid || nsds5replicalegacyconsumer || nsds5replicaname || nsds5replicaprotocoltimeout || nsds5replicapurgedelay || nsds5replicareferral || nsds5replicaroot || nsds5replicatombstonepurgeinterval || nsds5replicatype || nsds5task || nsstate || objectclass")(targetfilter = "(objectclass=nsds5replica)")(version 3.0;acl "permission:System: Read Replication Information";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
|
dn: dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "createtimestamp || entryusn || modifytimestamp")(targetfilter = "(objectclass=*)")(version 3.0;acl "permission:System: Read Timestamp and USN Operational Attributes";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
|
4
API.txt
4
API.txt
@ -2038,7 +2038,7 @@ option: Int('ipabaserid', attribute=True, cli_name='rid_base', multivalue=False,
|
|||||||
option: Int('ipaidrangesize', attribute=True, cli_name='range_size', multivalue=False, required=True)
|
option: Int('ipaidrangesize', attribute=True, cli_name='range_size', multivalue=False, required=True)
|
||||||
option: Str('ipanttrusteddomainname', attribute=False, cli_name='dom_name', multivalue=False, required=False)
|
option: Str('ipanttrusteddomainname', attribute=False, cli_name='dom_name', multivalue=False, required=False)
|
||||||
option: Str('ipanttrusteddomainsid', attribute=True, cli_name='dom_sid', multivalue=False, required=False)
|
option: Str('ipanttrusteddomainsid', attribute=True, cli_name='dom_sid', multivalue=False, required=False)
|
||||||
option: StrEnum('iparangetype', attribute=True, cli_name='type', multivalue=False, required=False, values=(u'ipa-ad-trust-posix', u'ipa-ad-trust', u'ipa-local', u'ipa-ad-winsync', u'ipa-ipa-trust'))
|
option: StrEnum('iparangetype', attribute=True, cli_name='type', multivalue=False, required=False, values=(u'ipa-ad-trust-posix', u'ipa-ad-trust', u'ipa-local'))
|
||||||
option: Int('ipasecondarybaserid', attribute=True, cli_name='secondary_rid_base', multivalue=False, required=False)
|
option: Int('ipasecondarybaserid', attribute=True, cli_name='secondary_rid_base', multivalue=False, required=False)
|
||||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||||
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
||||||
@ -2063,7 +2063,7 @@ option: Int('ipabaseid', attribute=True, autofill=False, cli_name='base_id', mul
|
|||||||
option: Int('ipabaserid', attribute=True, autofill=False, cli_name='rid_base', multivalue=False, query=True, required=False)
|
option: Int('ipabaserid', attribute=True, autofill=False, cli_name='rid_base', multivalue=False, query=True, required=False)
|
||||||
option: Int('ipaidrangesize', attribute=True, autofill=False, cli_name='range_size', multivalue=False, query=True, required=False)
|
option: Int('ipaidrangesize', attribute=True, autofill=False, cli_name='range_size', multivalue=False, query=True, required=False)
|
||||||
option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='dom_sid', multivalue=False, query=True, required=False)
|
option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='dom_sid', multivalue=False, query=True, required=False)
|
||||||
option: StrEnum('iparangetype', attribute=True, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'ipa-ad-trust-posix', u'ipa-ad-trust', u'ipa-local', u'ipa-ad-winsync', u'ipa-ipa-trust'))
|
option: StrEnum('iparangetype', attribute=True, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'ipa-ad-trust-posix', u'ipa-ad-trust', u'ipa-local'))
|
||||||
option: Int('ipasecondarybaserid', attribute=True, autofill=False, cli_name='secondary_rid_base', multivalue=False, query=True, required=False)
|
option: Int('ipasecondarybaserid', attribute=True, autofill=False, cli_name='secondary_rid_base', multivalue=False, query=True, required=False)
|
||||||
option: Flag('pkey_only?', autofill=True, default=False)
|
option: Flag('pkey_only?', autofill=True, default=False)
|
||||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||||
|
2
VERSION
2
VERSION
@ -20,7 +20,7 @@
|
|||||||
########################################################
|
########################################################
|
||||||
IPA_VERSION_MAJOR=4
|
IPA_VERSION_MAJOR=4
|
||||||
IPA_VERSION_MINOR=0
|
IPA_VERSION_MINOR=0
|
||||||
IPA_VERSION_RELEASE=0
|
IPA_VERSION_RELEASE=2
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# For 'pre' releases the version will be #
|
# For 'pre' releases the version will be #
|
||||||
|
@ -660,6 +660,7 @@ static int ipa_range_check_pre_op(Slapi_PBlock *pb, int modtype)
|
|||||||
break;
|
break;
|
||||||
case RANGE_CHECK_DIFFERENT_TYPE_IN_DOMAIN:
|
case RANGE_CHECK_DIFFERENT_TYPE_IN_DOMAIN:
|
||||||
errmsg = "New ID range has invalid type. All ranges in the same domain must be of the same type.";
|
errmsg = "New ID range has invalid type. All ranges in the same domain must be of the same type.";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
errmsg = "New range overlaps with existing one.";
|
errmsg = "New range overlaps with existing one.";
|
||||||
break;
|
break;
|
||||||
|
@ -66,6 +66,7 @@ struct otptoken {
|
|||||||
enum otptoken_type type;
|
enum otptoken_type type;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
uint64_t watermark;
|
||||||
unsigned int step;
|
unsigned int step;
|
||||||
int offset;
|
int offset;
|
||||||
} totp;
|
} totp;
|
||||||
@ -123,69 +124,21 @@ static const struct berval *entry_attr_get_berval(const Slapi_Entry* e,
|
|||||||
return slapi_value_get_berval(v);
|
return slapi_value_get_berval(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool validate(struct otptoken *token, time_t now, ssize_t step,
|
static bool writeattr(const struct otptoken *token, const char *attr,
|
||||||
uint32_t first, const uint32_t *second)
|
int value)
|
||||||
{
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
switch (token->type) {
|
|
||||||
case OTPTOKEN_TOTP:
|
|
||||||
step = (now + token->totp.offset) / token->totp.step + step;
|
|
||||||
break;
|
|
||||||
case OTPTOKEN_HOTP:
|
|
||||||
step = token->hotp.counter + step;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hotp(&token->token, step, &tmp))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (first != tmp)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (second == NULL)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!hotp(&token->token, step + 1, &tmp))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return *second == tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool writeback(struct otptoken *token, ssize_t step, bool sync)
|
|
||||||
{
|
{
|
||||||
Slapi_Value *svals[] = { NULL, NULL };
|
Slapi_Value *svals[] = { NULL, NULL };
|
||||||
Slapi_PBlock *pb = NULL;
|
Slapi_PBlock *pb = NULL;
|
||||||
Slapi_Mods *mods = NULL;
|
Slapi_Mods *mods = NULL;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
const char *attr;
|
|
||||||
int value;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (token->type) {
|
|
||||||
case OTPTOKEN_TOTP:
|
|
||||||
if (!sync)
|
|
||||||
return true;
|
|
||||||
attr = T("clockOffset");
|
|
||||||
value = token->totp.offset + step * token->totp.step;
|
|
||||||
break;
|
|
||||||
case OTPTOKEN_HOTP:
|
|
||||||
/* Having support for LDAP_MOD_INCREMENT could be helpful here. */
|
|
||||||
if (step < 0)
|
|
||||||
return false; /* NEVER go backwards! */
|
|
||||||
attr = H("counter");
|
|
||||||
value = token->hotp.counter + step;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the value. */
|
/* Create the value. */
|
||||||
svals[0] = slapi_value_new();
|
svals[0] = slapi_value_new();
|
||||||
if (slapi_value_set_int(svals[0], value) != 0)
|
if (slapi_value_set_int(svals[0], value) != 0) {
|
||||||
goto error;
|
slapi_value_free(&svals[0]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the mods. */
|
/* Create the mods. */
|
||||||
mods = slapi_mods_new();
|
mods = slapi_mods_new();
|
||||||
@ -203,26 +156,91 @@ static bool writeback(struct otptoken *token, ssize_t step, bool sync)
|
|||||||
if (ret != LDAP_SUCCESS)
|
if (ret != LDAP_SUCCESS)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Save our modifications to the object. */
|
|
||||||
switch (token->type) {
|
|
||||||
case OTPTOKEN_TOTP:
|
|
||||||
token->totp.offset = value;
|
|
||||||
break;
|
|
||||||
case OTPTOKEN_HOTP:
|
|
||||||
token->hotp.counter = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
slapi_pblock_destroy(pb);
|
slapi_pblock_destroy(pb);
|
||||||
slapi_mods_free(&mods);
|
slapi_mods_free(&mods);
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a token.
|
||||||
|
*
|
||||||
|
* If the second token code is specified, perform synchronization.
|
||||||
|
*/
|
||||||
|
static bool validate(struct otptoken *token, time_t now, ssize_t step,
|
||||||
|
uint32_t first, const uint32_t *second)
|
||||||
|
{
|
||||||
|
const char *attr;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
/* Calculate the absolute step. */
|
||||||
|
switch (token->type) {
|
||||||
|
case OTPTOKEN_TOTP:
|
||||||
|
attr = T("watermark");
|
||||||
|
step = (now + token->totp.offset) / token->totp.step + step;
|
||||||
|
if (token->totp.watermark > 0 && step < token->totp.watermark)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case OTPTOKEN_HOTP:
|
||||||
|
if (step < 0) /* NEVER go backwards! */
|
||||||
|
return false;
|
||||||
|
attr = H("counter");
|
||||||
|
step = token->hotp.counter + step;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate the first code. */
|
||||||
|
if (!hotp(&token->token, step++, &tmp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (first != tmp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Validate the second code if specified. */
|
||||||
|
if (second != NULL) {
|
||||||
|
if (!hotp(&token->token, step++, &tmp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (*second != tmp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Perform optional synchronization steps. */
|
||||||
|
switch (token->type) {
|
||||||
|
case OTPTOKEN_TOTP:
|
||||||
|
tmp = (step - now / token->totp.step) * token->totp.step;
|
||||||
|
if (!writeattr(token, T("clockOffset"), tmp))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the step value. */
|
||||||
|
if (!writeattr(token, attr, step))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Save our modifications to the object. */
|
||||||
|
switch (token->type) {
|
||||||
|
case OTPTOKEN_TOTP:
|
||||||
|
token->totp.watermark = step;
|
||||||
|
break;
|
||||||
|
case OTPTOKEN_HOTP:
|
||||||
|
token->hotp.counter = step;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void otptoken_free(struct otptoken *token)
|
static void otptoken_free(struct otptoken *token)
|
||||||
{
|
{
|
||||||
if (token == NULL)
|
if (token == NULL)
|
||||||
@ -303,6 +321,9 @@ static struct otptoken *otptoken_new(Slapi_ComponentId *id, Slapi_Entry *entry)
|
|||||||
/* Get offset. */
|
/* Get offset. */
|
||||||
token->totp.offset = slapi_entry_attr_get_int(entry, T("clockOffset"));
|
token->totp.offset = slapi_entry_attr_get_int(entry, T("clockOffset"));
|
||||||
|
|
||||||
|
/* Get watermark. */
|
||||||
|
token->totp.watermark = slapi_entry_attr_get_int(entry, T("watermark"));
|
||||||
|
|
||||||
/* Get step. */
|
/* Get step. */
|
||||||
token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep"));
|
token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep"));
|
||||||
if (token->totp.step == 0)
|
if (token->totp.step == 0)
|
||||||
@ -464,15 +485,11 @@ static bool otptoken_validate(struct otptoken *token, size_t steps,
|
|||||||
for (int i = 0; i <= steps; i++) {
|
for (int i = 0; i <= steps; i++) {
|
||||||
/* Validate the positive step. */
|
/* Validate the positive step. */
|
||||||
if (validate(token, now, i, code, NULL))
|
if (validate(token, now, i, code, NULL))
|
||||||
return writeback(token, i + 1, false);
|
return true;
|
||||||
|
|
||||||
/* Counter-based tokens must NEVER validate old steps! */
|
|
||||||
if (i == 0 || token->type == OTPTOKEN_HOTP)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Validate the negative step. */
|
/* Validate the negative step. */
|
||||||
if (validate(token, now, 0 - i, code, NULL))
|
if (validate(token, now, 0 - i, code, NULL))
|
||||||
return writeback(token, 0 - i + 1, false);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -538,15 +555,11 @@ static bool otptoken_sync(struct otptoken * const *tokens, size_t steps,
|
|||||||
for (int j = 0; tokens[j] != NULL; j++) {
|
for (int j = 0; tokens[j] != NULL; j++) {
|
||||||
/* Validate the positive step. */
|
/* Validate the positive step. */
|
||||||
if (validate(tokens[j], now, i, first_code, &second_code))
|
if (validate(tokens[j], now, i, first_code, &second_code))
|
||||||
return writeback(tokens[j], i + 2, true);
|
return true;
|
||||||
|
|
||||||
/* Counter-based tokens must NEVER validate old steps! */
|
|
||||||
if (i == 0 || tokens[j]->type == OTPTOKEN_HOTP)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Validate the negative step. */
|
/* Validate the negative step. */
|
||||||
if (validate(tokens[j], now, 0 - i, first_code, &second_code))
|
if (validate(tokens[j], now, 0 - i, first_code, &second_code))
|
||||||
return writeback(tokens[j], 0 - i + 2, true);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,13 +65,14 @@ BuildRequires: m2crypto
|
|||||||
BuildRequires: check
|
BuildRequires: check
|
||||||
BuildRequires: libsss_idmap-devel
|
BuildRequires: libsss_idmap-devel
|
||||||
BuildRequires: libsss_nss_idmap-devel
|
BuildRequires: libsss_nss_idmap-devel
|
||||||
BuildRequires: java-1.7.0-openjdk
|
BuildRequires: java-headless
|
||||||
BuildRequires: rhino
|
BuildRequires: rhino
|
||||||
BuildRequires: libverto-devel
|
BuildRequires: libverto-devel
|
||||||
BuildRequires: systemd
|
BuildRequires: systemd
|
||||||
BuildRequires: libunistring-devel
|
BuildRequires: libunistring-devel
|
||||||
BuildRequires: python-lesscpy
|
BuildRequires: python-lesscpy
|
||||||
BuildRequires: python-yubico
|
BuildRequires: python-yubico
|
||||||
|
BuildRequires: python-backports-ssl_match_hostname
|
||||||
|
|
||||||
%description
|
%description
|
||||||
IPA is an integrated solution to provide centrally managed Identity (machine,
|
IPA is an integrated solution to provide centrally managed Identity (machine,
|
||||||
@ -86,11 +87,11 @@ Group: System Environment/Base
|
|||||||
Requires: %{name}-python = %{version}-%{release}
|
Requires: %{name}-python = %{version}-%{release}
|
||||||
Requires: %{name}-client = %{version}-%{release}
|
Requires: %{name}-client = %{version}-%{release}
|
||||||
Requires: %{name}-admintools = %{version}-%{release}
|
Requires: %{name}-admintools = %{version}-%{release}
|
||||||
Requires: 389-ds-base >= 1.3.2.19
|
Requires: 389-ds-base >= 1.3.2.20
|
||||||
Requires: openldap-clients > 2.4.35-4
|
Requires: openldap-clients > 2.4.35-4
|
||||||
Requires: nss >= 3.14.3-12.0
|
Requires: nss >= 3.14.3-12.0
|
||||||
Requires: nss-tools >= 3.14.3-12.0
|
Requires: nss-tools >= 3.14.3-12.0
|
||||||
Requires: krb5-server >= 1.11.5-3
|
Requires: krb5-server >= 1.11.5-5
|
||||||
Requires: krb5-pkinit-openssl
|
Requires: krb5-pkinit-openssl
|
||||||
Requires: cyrus-sasl-gssapi%{?_isa}
|
Requires: cyrus-sasl-gssapi%{?_isa}
|
||||||
Requires: ntp
|
Requires: ntp
|
||||||
@ -122,8 +123,8 @@ Requires: python-dns
|
|||||||
Requires: zip
|
Requires: zip
|
||||||
Requires: policycoreutils >= %{POLICYCOREUTILSVER}
|
Requires: policycoreutils >= %{POLICYCOREUTILSVER}
|
||||||
Requires: tar
|
Requires: tar
|
||||||
Requires(pre): certmonger >= 0.65
|
Requires(pre): certmonger >= 0.75.13
|
||||||
Requires(pre): 389-ds-base >= 1.3.2.19
|
Requires(pre): 389-ds-base >= 1.3.2.20
|
||||||
Requires: fontawesome-fonts
|
Requires: fontawesome-fonts
|
||||||
Requires: open-sans-fonts
|
Requires: open-sans-fonts
|
||||||
|
|
||||||
@ -203,6 +204,7 @@ Requires: libsss_autofs
|
|||||||
Requires: autofs
|
Requires: autofs
|
||||||
Requires: libnfsidmap
|
Requires: libnfsidmap
|
||||||
Requires: nfs-utils
|
Requires: nfs-utils
|
||||||
|
Requires: python-backports-ssl_match_hostname
|
||||||
Requires(post): policycoreutils
|
Requires(post): policycoreutils
|
||||||
|
|
||||||
Obsoletes: ipa-client >= 1.0
|
Obsoletes: ipa-client >= 1.0
|
||||||
|
@ -23,8 +23,9 @@ attributeTypes: (2.16.840.1.113730.3.8.16.1.18 NAME 'ipatokenRadiusTimeout' DESC
|
|||||||
attributeTypes: (2.16.840.1.113730.3.8.16.1.19 NAME 'ipatokenRadiusRetries' DESC 'Number of allowed Retries' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
attributeTypes: (2.16.840.1.113730.3.8.16.1.19 NAME 'ipatokenRadiusRetries' DESC 'Number of allowed Retries' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
||||||
attributeTypes: (2.16.840.1.113730.3.8.16.1.20 NAME 'ipatokenUserMapAttribute' DESC 'Attribute to map from the user entry for RADIUS server authentication' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
attributeTypes: (2.16.840.1.113730.3.8.16.1.20 NAME 'ipatokenUserMapAttribute' DESC 'Attribute to map from the user entry for RADIUS server authentication' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
||||||
attributeTypes: (2.16.840.1.113730.3.8.16.1.21 NAME 'ipatokenHOTPcounter' DESC 'HOTP counter' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
attributeTypes: (2.16.840.1.113730.3.8.16.1.21 NAME 'ipatokenHOTPcounter' DESC 'HOTP counter' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
||||||
|
attributeTypes: (2.16.840.1.113730.3.8.16.1.22 NAME 'ipatokenTOTPwatermark' DESC 'TOTP watermark' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.16.2.1 NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ managedBy $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP')
|
objectClasses: (2.16.840.1.113730.3.8.16.2.1 NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ managedBy $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.16.2.2 NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) X-ORIGIN 'IPA OTP')
|
objectClasses: (2.16.840.1.113730.3.8.16.2.2 NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) MAY (ipatokenTOTPwatermark) X-ORIGIN 'IPA OTP')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.16.2.3 NAME 'ipatokenRadiusProxyUser' SUP top AUXILIARY DESC 'Radius Proxy User' MAY (ipatokenRadiusConfigLink $ ipatokenRadiusUserName) X-ORIGIN 'IPA OTP')
|
objectClasses: (2.16.840.1.113730.3.8.16.2.3 NAME 'ipatokenRadiusProxyUser' SUP top AUXILIARY DESC 'Radius Proxy User' MAY (ipatokenRadiusConfigLink $ ipatokenRadiusUserName) X-ORIGIN 'IPA OTP')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.16.2.4 NAME 'ipatokenRadiusConfiguration' SUP top STRUCTURAL DESC 'Proxy Radius Configuration' MUST (cn $ ipatokenRadiusServer $ ipatokenRadiusSecret) MAY (description $ ipatokenRadiusTimeout $ ipatokenRadiusRetries $ ipatokenUserMapAttribute) X-ORIGIN 'IPA OTP')
|
objectClasses: (2.16.840.1.113730.3.8.16.2.4 NAME 'ipatokenRadiusConfiguration' SUP top STRUCTURAL DESC 'Proxy Radius Configuration' MUST (cn $ ipatokenRadiusServer $ ipatokenRadiusSecret) MAY (description $ ipatokenRadiusTimeout $ ipatokenRadiusRetries $ ipatokenUserMapAttribute) X-ORIGIN 'IPA OTP')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.16.2.5 NAME 'ipatokenHOTP' SUP ipaToken STRUCTURAL DESC 'HOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenHOTPcounter) X-ORIGIN 'IPA OTP')
|
objectClasses: (2.16.840.1.113730.3.8.16.2.5 NAME 'ipatokenHOTP' SUP ipaToken STRUCTURAL DESC 'HOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenHOTPcounter) X-ORIGIN 'IPA OTP')
|
||||||
|
@ -25,7 +25,8 @@ import os
|
|||||||
import krbV
|
import krbV
|
||||||
from ipapython.ipa_log_manager import *
|
from ipapython.ipa_log_manager import *
|
||||||
|
|
||||||
from ipaserver.install import replication, installutils, bindinstance
|
from ipaserver.install import (replication, installutils, bindinstance,
|
||||||
|
cainstance, certs)
|
||||||
from ipalib import api, errors, util
|
from ipalib import api, errors, util
|
||||||
from ipalib.constants import CACERT
|
from ipalib.constants import CACERT
|
||||||
from ipapython import ipautil, ipaldap, version, dogtag
|
from ipapython import ipautil, ipaldap, version, dogtag
|
||||||
@ -41,7 +42,8 @@ commands = {
|
|||||||
"del": (1, 1, "<master fqdn>",
|
"del": (1, 1, "<master fqdn>",
|
||||||
"must provide hostname of master to delete"),
|
"must provide hostname of master to delete"),
|
||||||
"re-initialize": (0, 0, "", ""),
|
"re-initialize": (0, 0, "", ""),
|
||||||
"force-sync":(0, 0, "", "")
|
"force-sync": (0, 0, "", ""),
|
||||||
|
"set-renewal-master": (0, 1, "[master fqdn]", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -272,7 +274,12 @@ def del_master(realm, hostname, options):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
sys.exit("There were issues removing a connection: %s" % e)
|
sys.exit("There were issues removing a connection: %s" % e)
|
||||||
|
|
||||||
# 6. And clean up the removed replica DNS entries if any.
|
# 6. Pick CA renewal master
|
||||||
|
ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
|
||||||
|
if ca.is_renewal_master(hostname):
|
||||||
|
ca.set_renewal_master(options.host)
|
||||||
|
|
||||||
|
# 7. And clean up the removed replica DNS entries if any.
|
||||||
try:
|
try:
|
||||||
if bindinstance.dns_container_exists(options.host, api.env.basedn,
|
if bindinstance.dns_container_exists(options.host, api.env.basedn,
|
||||||
dm_password=options.dirman_passwd):
|
dm_password=options.dirman_passwd):
|
||||||
@ -369,6 +376,21 @@ def force_sync(realm, thishost, fromhost, dirman_passwd):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
sys.exit(str(e))
|
sys.exit(str(e))
|
||||||
|
|
||||||
|
def set_renewal_master(realm, replica):
|
||||||
|
if not replica:
|
||||||
|
replica = installutils.get_fqdn()
|
||||||
|
|
||||||
|
ca = cainstance.CAInstance(realm, certs.NSS_DIR)
|
||||||
|
if ca.is_renewal_master(replica):
|
||||||
|
sys.exit("%s is already the renewal master" % replica)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ca.set_renewal_master(replica)
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to set renewal master to %s: %s" % (replica, e))
|
||||||
|
|
||||||
|
print "%s is now the renewal master" % replica
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
options, args = parse_options()
|
options, args = parse_options()
|
||||||
|
|
||||||
@ -433,6 +455,11 @@ def main():
|
|||||||
replica1 = host
|
replica1 = host
|
||||||
replica2 = args[1]
|
replica2 = args[1]
|
||||||
del_link(realm, replica1, replica2, dirman_passwd, options.force)
|
del_link(realm, replica1, replica2, dirman_passwd, options.force)
|
||||||
|
elif args[0] == 'set-renewal-master':
|
||||||
|
replica = None
|
||||||
|
if len(args) > 1:
|
||||||
|
replica = args[1]
|
||||||
|
set_renewal_master(realm, replica)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
main()
|
main()
|
||||||
|
@ -699,7 +699,6 @@ def main():
|
|||||||
CA.configure_certmonger_renewal()
|
CA.configure_certmonger_renewal()
|
||||||
CA.import_ra_cert(dir + "/ra.p12")
|
CA.import_ra_cert(dir + "/ra.p12")
|
||||||
CA.fix_ra_perms()
|
CA.fix_ra_perms()
|
||||||
services.knownservices.httpd.restart()
|
|
||||||
|
|
||||||
# The DS instance is created before the keytab, add the SSL cert we
|
# The DS instance is created before the keytab, add the SSL cert we
|
||||||
# generated
|
# generated
|
||||||
|
@ -28,7 +28,7 @@ import socket
|
|||||||
|
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipaserver.install import replication, dsinstance, installutils
|
from ipaserver.install import replication, dsinstance, installutils
|
||||||
from ipaserver.install import bindinstance
|
from ipaserver.install import bindinstance, cainstance, certs
|
||||||
from ipaserver.plugins import ldap2
|
from ipaserver.plugins import ldap2
|
||||||
from ipapython import version, ipaldap
|
from ipapython import version, ipaldap
|
||||||
from ipalib import api, errors, util
|
from ipalib import api, errors, util
|
||||||
@ -665,6 +665,7 @@ def del_master(realm, hostname, options):
|
|||||||
# Check that we are not leaving the installation without CA and/or DNS
|
# Check that we are not leaving the installation without CA and/or DNS
|
||||||
this_services = []
|
this_services = []
|
||||||
other_services = []
|
other_services = []
|
||||||
|
ca_hostname = None
|
||||||
|
|
||||||
for master_cn in [m.single_value['cn'] for m in masters]:
|
for master_cn in [m.single_value['cn'] for m in masters]:
|
||||||
master_dn = DN(('cn', master_cn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ipautil.realm_to_suffix(realm))
|
master_dn = DN(('cn', master_cn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ipautil.realm_to_suffix(realm))
|
||||||
@ -679,6 +680,8 @@ def del_master(realm, hostname, options):
|
|||||||
this_services = services_cns
|
this_services = services_cns
|
||||||
else:
|
else:
|
||||||
other_services.append(services_cns)
|
other_services.append(services_cns)
|
||||||
|
if ca_hostname is None and 'CA' in services_cns:
|
||||||
|
ca_hostname = master_cn
|
||||||
|
|
||||||
if 'CA' in this_services and not any(['CA' in o for o in other_services]):
|
if 'CA' in this_services and not any(['CA' in o for o in other_services]):
|
||||||
print "Deleting this server is not allowed as it would leave your installation without a CA."
|
print "Deleting this server is not allowed as it would leave your installation without a CA."
|
||||||
@ -688,6 +691,14 @@ def del_master(realm, hostname, options):
|
|||||||
print "Deleting this server will leave your installation without a DNS."
|
print "Deleting this server will leave your installation without a DNS."
|
||||||
if not options.force and not ipautil.user_input("Continue to delete?", False):
|
if not options.force and not ipautil.user_input("Continue to delete?", False):
|
||||||
sys.exit("Deletion aborted")
|
sys.exit("Deletion aborted")
|
||||||
|
|
||||||
|
# Pick CA renewal master
|
||||||
|
ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
|
||||||
|
if ca.is_renewal_master(hostname):
|
||||||
|
try:
|
||||||
|
ca.set_renewal_master(options.host)
|
||||||
|
except errors.NotFound:
|
||||||
|
ca.set_renewal_master(ca_hostname)
|
||||||
else:
|
else:
|
||||||
print "Skipping calculation to determine if one or more masters would be orphaned."
|
print "Skipping calculation to determine if one or more masters would be orphaned."
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ from ipapython import sysrestore
|
|||||||
from ipapython.ipautil import *
|
from ipapython.ipautil import *
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipapython import dogtag
|
from ipapython import dogtag
|
||||||
from ipalib import api, errors, util
|
from ipalib import api, errors, util, x509
|
||||||
from ipapython.config import IPAOptionParser
|
from ipapython.config import IPAOptionParser
|
||||||
from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file
|
from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file
|
||||||
from ipalib.util import validate_domain_name
|
from ipalib.util import validate_domain_name
|
||||||
@ -179,11 +179,11 @@ def parse_options():
|
|||||||
|
|
||||||
cert_group = OptionGroup(parser, "certificate system options")
|
cert_group = OptionGroup(parser, "certificate system options")
|
||||||
cert_group.add_option("", "--external-ca", dest="external_ca", action="store_true",
|
cert_group.add_option("", "--external-ca", dest="external_ca", action="store_true",
|
||||||
default=False, help="Generate a CSR to be signed by an external CA")
|
default=False, help="Generate a CSR for the IPA CA certificate to be signed by an external CA")
|
||||||
cert_group.add_option("", "--external_cert_file", dest="external_cert_file",
|
cert_group.add_option("", "--external_cert_file", dest="external_cert_file",
|
||||||
help="PEM file containing a certificate signed by the external CA")
|
help="File containing the IPA CA certificate signed by the external CA in PEM format")
|
||||||
cert_group.add_option("", "--external_ca_file", dest="external_ca_file",
|
cert_group.add_option("", "--external_ca_file", dest="external_ca_file",
|
||||||
help="PEM file containing the external CA chain")
|
help="File containing the external CA certificate chain in PEM format")
|
||||||
cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
|
cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
|
||||||
default=True, help="disables pkinit setup steps")
|
default=True, help="disables pkinit setup steps")
|
||||||
cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
|
cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
|
||||||
@ -199,7 +199,7 @@ def parse_options():
|
|||||||
cert_group.add_option("--pkinit_pin", dest="pkinit_pin",
|
cert_group.add_option("--pkinit_pin", dest="pkinit_pin",
|
||||||
help="The password of the Kerberos KDC PKCS#12 file")
|
help="The password of the Kerberos KDC PKCS#12 file")
|
||||||
cert_group.add_option("--root-ca-file", dest="root_ca_file",
|
cert_group.add_option("--root-ca-file", dest="root_ca_file",
|
||||||
help="PEM file with root CA certificate(s) to trust")
|
help="PEM file containing the CA certificate for the PKCS#12 files")
|
||||||
cert_group.add_option("--subject", action="callback", callback=subject_callback,
|
cert_group.add_option("--subject", action="callback", callback=subject_callback,
|
||||||
type="string",
|
type="string",
|
||||||
help="The certificate subject base (default O=<realm-name>)")
|
help="The certificate subject base (default O=<realm-name>)")
|
||||||
@ -289,10 +289,6 @@ def parse_options():
|
|||||||
if options.pkinit_pkcs12 and options.pkinit_pin is None:
|
if options.pkinit_pkcs12 and options.pkinit_pin is None:
|
||||||
parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
|
parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
|
||||||
|
|
||||||
if options.dirsrv_pkcs12 and not options.root_ca_file:
|
|
||||||
parser.error(
|
|
||||||
"--root-ca-file must be given with the PKCS#12 options.")
|
|
||||||
|
|
||||||
if (options.external_cert_file or options.external_ca_file) and options.dirsrv_pkcs12:
|
if (options.external_cert_file or options.external_ca_file) and options.dirsrv_pkcs12:
|
||||||
parser.error(
|
parser.error(
|
||||||
"PKCS#12 options cannot be used with the external CA options.")
|
"PKCS#12 options cannot be used with the external CA options.")
|
||||||
@ -513,6 +509,10 @@ def uninstall():
|
|||||||
os.remove(ANSWER_CACHE)
|
os.remove(ANSWER_CACHE)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
os.remove(paths.ROOT_IPA_CSR)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# ipa-client-install removes /etc/ipa/default.conf
|
# ipa-client-install removes /etc/ipa/default.conf
|
||||||
|
|
||||||
@ -686,13 +686,21 @@ def main():
|
|||||||
|
|
||||||
if options.external_ca:
|
if options.external_ca:
|
||||||
if cainstance.is_step_one_done():
|
if cainstance.is_step_one_done():
|
||||||
print "CA is already installed.\nRun the installer with --external_cert_file and --external_ca_file."
|
print ("CA is already installed.\nRun the installer with "
|
||||||
|
"--external_cert_file and --external_ca_file.")
|
||||||
|
sys.exit(1)
|
||||||
|
if ipautil.file_exists(paths.ROOT_IPA_CSR):
|
||||||
|
print ("CA CSR file %s already exists.\nIn order to continue "
|
||||||
|
"remove the file and run the installer again." %
|
||||||
|
paths.ROOT_IPA_CSR)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif options.external_cert_file:
|
elif options.external_cert_file:
|
||||||
if not cainstance.is_step_one_done():
|
if not cainstance.is_step_one_done():
|
||||||
# This can happen if someone passes external_ca_file without
|
# This can happen if someone passes external_ca_file without
|
||||||
# already having done the first stage of the CA install.
|
# already having done the first stage of the CA install.
|
||||||
print "CA is not installed yet. To install with an external CA is a two-stage process.\nFirst run the installer with --external-ca."
|
print ("CA is not installed yet. To install with an external CA "
|
||||||
|
"is a two-stage process.\nFirst run the installer with "
|
||||||
|
"--external-ca.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# This will override any settings passed in on the cmdline
|
# This will override any settings passed in on the cmdline
|
||||||
@ -901,7 +909,7 @@ def main():
|
|||||||
if options.http_pin is None:
|
if options.http_pin is None:
|
||||||
sys.exit("%s unlock password required" % options.http_pkcs12)
|
sys.exit("%s unlock password required" % options.http_pkcs12)
|
||||||
http_pkcs12_info = (options.http_pkcs12, options.http_pin)
|
http_pkcs12_info = (options.http_pkcs12, options.http_pin)
|
||||||
http_cert_name = installutils.check_pkcs12(
|
http_ca_cert = installutils.check_pkcs12(
|
||||||
http_pkcs12_info, ca_file, host_name)
|
http_pkcs12_info, ca_file, host_name)
|
||||||
|
|
||||||
if options.dirsrv_pkcs12:
|
if options.dirsrv_pkcs12:
|
||||||
@ -912,7 +920,7 @@ def main():
|
|||||||
if options.dirsrv_pin is None:
|
if options.dirsrv_pin is None:
|
||||||
sys.exit("%s unlock password required" % options.dirsrv_pkcs12)
|
sys.exit("%s unlock password required" % options.dirsrv_pkcs12)
|
||||||
dirsrv_pkcs12_info = (options.dirsrv_pkcs12, options.dirsrv_pin)
|
dirsrv_pkcs12_info = (options.dirsrv_pkcs12, options.dirsrv_pin)
|
||||||
dirsrv_cert_name = installutils.check_pkcs12(
|
dirsrv_ca_cert = installutils.check_pkcs12(
|
||||||
dirsrv_pkcs12_info, ca_file, host_name)
|
dirsrv_pkcs12_info, ca_file, host_name)
|
||||||
|
|
||||||
if options.pkinit_pkcs12:
|
if options.pkinit_pkcs12:
|
||||||
@ -924,6 +932,11 @@ def main():
|
|||||||
sys.exit("%s unlock password required" % options.pkinit_pkcs12)
|
sys.exit("%s unlock password required" % options.pkinit_pkcs12)
|
||||||
pkinit_pkcs12_info = (options.pkinit_pkcs12, options.pkinit_pin)
|
pkinit_pkcs12_info = (options.pkinit_pkcs12, options.pkinit_pin)
|
||||||
|
|
||||||
|
if (options.http_pkcs12 and options.dirsrv_pkcs12 and
|
||||||
|
http_ca_cert != dirsrv_ca_cert):
|
||||||
|
sys.exit("%s and %s are not signed by the same CA certificate" %
|
||||||
|
(options.http_pkcs12, options.dirsrv_pkcs12))
|
||||||
|
|
||||||
if not options.dm_password:
|
if not options.dm_password:
|
||||||
dm_password = read_dm_password()
|
dm_password = read_dm_password()
|
||||||
|
|
||||||
@ -1053,8 +1066,7 @@ def main():
|
|||||||
ntp.create_instance()
|
ntp.create_instance()
|
||||||
|
|
||||||
if options.dirsrv_pkcs12:
|
if options.dirsrv_pkcs12:
|
||||||
ds = dsinstance.DsInstance(fstore=fstore,
|
ds = dsinstance.DsInstance(fstore=fstore)
|
||||||
cert_nickname=dirsrv_cert_name)
|
|
||||||
ds.create_instance(realm_name, host_name, domain_name,
|
ds.create_instance(realm_name, host_name, domain_name,
|
||||||
dm_password, dirsrv_pkcs12_info,
|
dm_password, dirsrv_pkcs12_info,
|
||||||
idstart=options.idstart, idmax=options.idmax,
|
idstart=options.idstart, idmax=options.idmax,
|
||||||
@ -1108,7 +1120,7 @@ def main():
|
|||||||
ca.publish_ca_cert(CACERT)
|
ca.publish_ca_cert(CACERT)
|
||||||
else:
|
else:
|
||||||
# Put the CA cert where other instances expect it
|
# Put the CA cert where other instances expect it
|
||||||
shutil.copy(options.root_ca_file, CACERT)
|
x509.write_certificate(http_ca_cert, CACERT)
|
||||||
os.chmod(CACERT, 0444)
|
os.chmod(CACERT, 0444)
|
||||||
|
|
||||||
# we now need to enable ssl on the ds
|
# we now need to enable ssl on the ds
|
||||||
|
@ -598,6 +598,11 @@ def named_enable_dnssec():
|
|||||||
"""
|
"""
|
||||||
Enable dnssec in named.conf
|
Enable dnssec in named.conf
|
||||||
"""
|
"""
|
||||||
|
if not bindinstance.named_conf_exists():
|
||||||
|
# DNS service may not be configured
|
||||||
|
root_logger.info('DNS is not configured')
|
||||||
|
return False
|
||||||
|
|
||||||
if not sysupgrade.get_upgrade_state('named.conf', 'dnssec_enabled'):
|
if not sysupgrade.get_upgrade_state('named.conf', 'dnssec_enabled'):
|
||||||
root_logger.info('[Enabling "dnssec-enable" configuration in DNS]')
|
root_logger.info('[Enabling "dnssec-enable" configuration in DNS]')
|
||||||
try:
|
try:
|
||||||
@ -672,24 +677,25 @@ def certificate_renewal_update(ca):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# State not set, lets see if we are already configured
|
# State not set, lets see if we are already configured
|
||||||
for nss_dir, nickname, ca_name, pre_command, post_command in requests:
|
for request in requests:
|
||||||
criteria = (
|
nss_dir, nickname, ca_name, pre_command, post_command = request
|
||||||
('cert_storage_location', nss_dir, certmonger.NPATH),
|
criteria = {
|
||||||
('cert_nickname', nickname, None),
|
'cert-database': nss_dir,
|
||||||
('ca_name', ca_name, None),
|
'cert-nickname': nickname,
|
||||||
)
|
'ca-name': ca_name,
|
||||||
|
}
|
||||||
request_id = certmonger.get_request_id(criteria)
|
request_id = certmonger.get_request_id(criteria)
|
||||||
if request_id is None:
|
if request_id is None:
|
||||||
break
|
break
|
||||||
|
|
||||||
val = certmonger.get_request_value(request_id, 'pre_certsave_command')
|
val = certmonger.get_request_value(request_id, 'cert-presave-command')
|
||||||
if val is not None:
|
if val is not None:
|
||||||
val = val.split(' ', 1)[0]
|
val = val.split(' ', 1)[0]
|
||||||
val = os.path.basename(val)
|
val = os.path.basename(val)
|
||||||
if pre_command != val:
|
if pre_command != val:
|
||||||
break
|
break
|
||||||
|
|
||||||
val = certmonger.get_request_value(request_id, 'post_certsave_command')
|
val = certmonger.get_request_value(request_id, 'post-certsave-command')
|
||||||
if val is not None:
|
if val is not None:
|
||||||
val = val.split(' ', 1)[0]
|
val = val.split(' ', 1)[0]
|
||||||
val = os.path.basename(val)
|
val = os.path.basename(val)
|
||||||
@ -1074,6 +1080,7 @@ def main():
|
|||||||
sub_dict['SUBJECT_BASE'] = subject_base
|
sub_dict['SUBJECT_BASE'] = subject_base
|
||||||
|
|
||||||
ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
|
ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
|
||||||
|
ca.backup_config()
|
||||||
|
|
||||||
# migrate CRL publish dir before the location in ipa.conf is updated
|
# migrate CRL publish dir before the location in ipa.conf is updated
|
||||||
ca_restart = migrate_crl_publish_dir(ca)
|
ca_restart = migrate_crl_publish_dir(ca)
|
||||||
|
@ -42,6 +42,9 @@ Manages the CA replication agreements of an IPA server.
|
|||||||
\fBforce\-sync\fR
|
\fBforce\-sync\fR
|
||||||
\- Immediately flush any data to be replicated from a server specified with the \-\-from option
|
\- Immediately flush any data to be replicated from a server specified with the \-\-from option
|
||||||
.TP
|
.TP
|
||||||
|
\fBset\-renewal\-master\fR [SERVER]
|
||||||
|
\- Set CA server which handles renewal of CA subsystem certificates to SERVER
|
||||||
|
.TP
|
||||||
The connect and disconnect options are used to manage the replication topology. When a replica is created it is only connected with the master that created it. The connect option may be used to connect it to other existing replicas.
|
The connect and disconnect options are used to manage the replication topology. When a replica is created it is only connected with the master that created it. The connect option may be used to connect it to other existing replicas.
|
||||||
.TP
|
.TP
|
||||||
The disconnect option cannot be used to remove the last link of a replica. To remove a replica from the topology use the del option.
|
The disconnect option cannot be used to remove the last link of a replica. To remove a replica from the topology use the del option.
|
||||||
|
@ -85,13 +85,17 @@ An unattended installation that will never prompt for user input
|
|||||||
.SS "CERTIFICATE SYSTEM OPTIONS"
|
.SS "CERTIFICATE SYSTEM OPTIONS"
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-external\-ca\fR
|
\fB\-\-external\-ca\fR
|
||||||
Generate a CSR to be signed by an external CA
|
Generate a CSR for the IPA CA certificate to be signed by an external CA.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-external_cert_file\fR=\fIFILE\fR
|
\fB\-\-external_cert_file\fR=\fIFILE\fR
|
||||||
PEM file containing a certificate signed by the external CA. Must be given with \-\-external_ca_file.
|
File containing the IPA CA certificate signed by the external CA in PEM format. Must be given with \-\-external_ca_file.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-external_ca_file\fR=\fIFILE\fR
|
\fB\-\-external_ca_file\fR=\fIFILE\fR
|
||||||
PEM file containing the external CA chain
|
File containing the external CA certificate chain in PEM format. Must be given with \-\-external_cert_file.
|
||||||
|
|
||||||
|
If the CA certificate chain is in PKCS#7 format you can convert it to PEM using:
|
||||||
|
|
||||||
|
openssl pkcs7 -in PKCS7_FILE -print_certs -out PEM_FILE
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-pkinit\fR
|
\fB\-\-no\-pkinit\fR
|
||||||
Disables pkinit setup steps
|
Disables pkinit setup steps
|
||||||
@ -114,6 +118,9 @@ The password of the Apache Server PKCS#12 file
|
|||||||
\fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
|
\fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
|
||||||
The password of the Kerberos KDC PKCS#12 file
|
The password of the Kerberos KDC PKCS#12 file
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-root\-ca\-file\fR=\fIFILE\fR
|
||||||
|
PEM file containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC SSL certificates. Use this option if the CA certificate is not present in the PKCS#12 files.
|
||||||
|
.TP
|
||||||
\fB\-\-subject\fR=\fISUBJECT\fR
|
\fB\-\-subject\fR=\fISUBJECT\fR
|
||||||
The certificate subject base (default O=REALM.NAME)
|
The certificate subject base (default O=REALM.NAME)
|
||||||
|
|
||||||
|
@ -189,7 +189,6 @@ div[name=settings].facet-group li a {
|
|||||||
/* --- Facet error --- */
|
/* --- Facet error --- */
|
||||||
|
|
||||||
.facet-error {
|
.facet-error {
|
||||||
padding: 2em 15em;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +198,10 @@ div[name=settings].facet-group li a {
|
|||||||
|
|
||||||
.facet-error .error-details {
|
.facet-error .error-details {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
font-family: monospace;
|
}
|
||||||
|
|
||||||
|
.facet-error .error-details code {
|
||||||
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Search Facet ---- */
|
/* ---- Search Facet ---- */
|
||||||
@ -252,15 +254,6 @@ div[name=settings].facet-group li a {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button-disabled,
|
|
||||||
.action-button-disabled:focus,
|
|
||||||
.action-button-disabled:hover {
|
|
||||||
color: gray;
|
|
||||||
cursor: default;
|
|
||||||
text-decoration: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aci-attribute-table tbody {
|
.aci-attribute-table tbody {
|
||||||
height: 10em;
|
height: 10em;
|
||||||
}
|
}
|
||||||
@ -345,7 +338,7 @@ table.scrollable tbody {
|
|||||||
.option_widget {
|
.option_widget {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0 0 0 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option_widget.nested {
|
.option_widget.nested {
|
||||||
@ -365,6 +358,19 @@ table.scrollable tbody {
|
|||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.option_widget.columns.attribute_widget {
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 36em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option_widget.columns.attribute_widget > li {
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
min-width: 90px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
.combobox-widget-input {
|
.combobox-widget-input {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.global-activity-indicator {
|
.global-activity-indicator {
|
||||||
|
|
||||||
bottom: initial;
|
bottom: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -247,26 +247,26 @@ define([
|
|||||||
|
|
||||||
on_phase_error: function(error) {
|
on_phase_error: function(error) {
|
||||||
|
|
||||||
window.console.error(error);
|
|
||||||
error = error || {};
|
error = error || {};
|
||||||
var name = error.name || 'Runtime error';
|
var name = error.name || 'Runtime error';
|
||||||
var error_container = $('<div/>', {
|
var error_container = $('<div/>', {
|
||||||
'class': 'facet-content facet-error'
|
'class': 'container facet-content facet-error'
|
||||||
}).appendTo($('.content').empty());
|
}).appendTo($('.app-container .content').empty());
|
||||||
error_container.append('<h1>'+name+'</h1>');
|
error_container.append('<h1>'+name+'</h1>');
|
||||||
var details = $('<div/>', {
|
var details = $('<div/>', {
|
||||||
'class': 'error-details'
|
'class': 'error-details'
|
||||||
}).appendTo(error_container);
|
}).appendTo(error_container);
|
||||||
|
|
||||||
details.append('<p> Web UI got in unrecoverable state during "'+error.phase+'" phase.</p>');
|
details.append('<p> Web UI got in unrecoverable state during "'+error.phase+'" phase.</p>');
|
||||||
|
if (error.name) window.console.error(error.name);
|
||||||
if (error.results) {
|
if (error.results) {
|
||||||
var msg = {
|
var msg = error.results.message;
|
||||||
message: error.results.message,
|
var stack = error.results.stack.toString();
|
||||||
stack: error.results.stack
|
window.console.error(msg);
|
||||||
};
|
window.console.error(stack);
|
||||||
details.append('<strong>Technical details:</strong>');
|
details.append('<h3>Technical details:</h3>');
|
||||||
details.append('<p>'+JSON.stringify(msg)+'</p>');
|
details.append($('<div/>', { text: error.results.message }));
|
||||||
|
details.append($('<div/>').append($('<code/>', { text: stack })));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -544,9 +544,17 @@ return {
|
|||||||
aci.attributes_widget = function(spec) {
|
aci.attributes_widget = function(spec) {
|
||||||
|
|
||||||
spec = spec || {};
|
spec = spec || {};
|
||||||
|
spec.layout = spec.layout || 'columns attribute_widget';
|
||||||
|
spec.sort = spec.sort === undefined ? true : spec.sort;
|
||||||
|
|
||||||
var that = IPA.checkboxes_widget(spec);
|
var that = IPA.checkboxes_widget(spec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional options which are not defined in metadata
|
||||||
|
* @property {string[]}
|
||||||
|
*/
|
||||||
|
that.custom_options = spec.custom_options || [];
|
||||||
|
|
||||||
that.object_type = spec.object_type;
|
that.object_type = spec.object_type;
|
||||||
that.skip_unmatched = spec.skip_unmatched === undefined ? false : spec.skip_unmatched;
|
that.skip_unmatched = spec.skip_unmatched === undefined ? false : spec.skip_unmatched;
|
||||||
|
|
||||||
@ -554,79 +562,93 @@ aci.attributes_widget = function(spec) {
|
|||||||
|
|
||||||
that.create = function(container) {
|
that.create = function(container) {
|
||||||
that.container = container;
|
that.container = container;
|
||||||
|
that.widget_create(container);
|
||||||
|
|
||||||
var attr_container = $('<div/>', {
|
that.controls = $('<div/>', {
|
||||||
'class': 'aci-attribute-table-container'
|
'class': 'form-inline controls'
|
||||||
}).appendTo(container);
|
});
|
||||||
|
that.controls.appendTo(container);
|
||||||
that.$node = that.table = $('<table/>', {
|
that.create_search_filter(that.controls);
|
||||||
id: id,
|
that.create_add_control(that.controls);
|
||||||
name: that.name,
|
|
||||||
'class': 'table table-bordered table-condensed aci-attribute-table scrollable'
|
|
||||||
}).
|
|
||||||
append('<thead/>').
|
|
||||||
append('<tbody/>').
|
|
||||||
appendTo(attr_container);
|
|
||||||
|
|
||||||
var tr = $('<tr></tr>').appendTo($('thead', that.table));
|
|
||||||
|
|
||||||
var th = $('<th/>').appendTo(tr);
|
|
||||||
IPA.standalone_option({
|
|
||||||
type: "checkbox",
|
|
||||||
click: function() {
|
|
||||||
$('.aci-attribute', that.table).
|
|
||||||
prop('checked', $(this).prop('checked'));
|
|
||||||
that.value_changed.notify([], that);
|
|
||||||
that.emit('value-change', { source: that });
|
|
||||||
}
|
|
||||||
}, th);
|
|
||||||
|
|
||||||
tr.append($('<th/>', {
|
|
||||||
'class': 'aci-attribute-column',
|
|
||||||
html: text.get('@i18n:objects.aci.attribute')
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (that.undo) {
|
if (that.undo) {
|
||||||
that.create_undo(container);
|
that.create_undo(that.controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (that.object_type) {
|
that.owb_create(container);
|
||||||
that.populate(that.object_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
that.create_error_link(container);
|
that.create_error_link(container);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.create_options = function(options) {
|
that.create_search_filter = function(container) {
|
||||||
var tbody = $('tbody', that.table);
|
var filter_container = $('<div/>', {
|
||||||
|
'class': 'search-filter'
|
||||||
|
});
|
||||||
|
|
||||||
for (var i=0; i<options.length ; i++){
|
that.filter = $('<input/>', {
|
||||||
var option = options[i];
|
type: 'text',
|
||||||
var value = option.value.toLowerCase();
|
name: 'filter',
|
||||||
var tr = $('<tr/>').appendTo(tbody);
|
'class': 'form-control',
|
||||||
|
placeholder: text.get('@i18n:objects.permission.filter')
|
||||||
|
}).appendTo(filter_container);
|
||||||
|
|
||||||
var td = $('<td/>').appendTo(tr);
|
that.filter.keyup(function(e) {
|
||||||
var name = that.get_input_name();
|
that.filter_options();
|
||||||
var id = that._option_next_id + name;
|
});
|
||||||
var opt = IPA.standalone_option({
|
|
||||||
id: id,
|
var find_button = IPA.action_button({
|
||||||
type: 'checkbox',
|
name: 'find',
|
||||||
name: name,
|
icon: 'fa-search',
|
||||||
value: value,
|
click: function() {
|
||||||
'class': 'aci-attribute',
|
that.filter_options();
|
||||||
change: function() {
|
return false;
|
||||||
that.value_changed.notify([], that);
|
|
||||||
that.emit('value-change', { source: that });
|
|
||||||
}
|
}
|
||||||
}, td);
|
}).appendTo(filter_container);
|
||||||
td = $('<td/>').appendTo(tr);
|
|
||||||
td.append($('<label/>',{
|
filter_container.appendTo(container);
|
||||||
text: value,
|
};
|
||||||
'for': id
|
|
||||||
}));
|
that.create_add_control = function(container) {
|
||||||
option.input_node = opt[0];
|
|
||||||
that.new_option_id();
|
that.add_button = IPA.button({
|
||||||
|
label: '@i18n:buttons.add',
|
||||||
|
click: that.show_add_dialog
|
||||||
|
});
|
||||||
|
container.append(' ');
|
||||||
|
that.add_button.appendTo(container);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.show_add_dialog = function() {
|
||||||
|
|
||||||
|
var dialog = IPA.form_dialog({
|
||||||
|
name: "add_option",
|
||||||
|
title: "@i18n:objects.permission.add_custom_attr",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'attr',
|
||||||
|
label: '@i18n:objects.permission.attribute',
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
dialog.on_confirm = function() {
|
||||||
|
if (!dialog.validate()) return;
|
||||||
|
var attr = dialog.get_field('attr');
|
||||||
|
var value = attr.get_value()[0];
|
||||||
|
that.add_custom_option(value, false, true, true);
|
||||||
|
dialog.close();
|
||||||
|
};
|
||||||
|
dialog.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
that.filter_options = function() {
|
||||||
|
$("li", that.$node).each(function() {
|
||||||
|
var item = $(this);
|
||||||
|
if(item.find('input').val().indexOf(that.filter.val()) === -1) {
|
||||||
|
item.css('display','none');
|
||||||
|
} else {
|
||||||
|
item.css('display','inline');
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
@ -646,14 +668,12 @@ aci.attributes_widget = function(spec) {
|
|||||||
|
|
||||||
that.populate(that.object_type);
|
that.populate(that.object_type);
|
||||||
that.append();
|
that.append();
|
||||||
that.create_options(that.options);
|
that.owb_create(that.container);
|
||||||
that.owb_update(values);
|
that.owb_update(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.populate = function(object_type) {
|
that.populate = function(object_type) {
|
||||||
|
|
||||||
$('tbody tr', that.table).remove();
|
|
||||||
|
|
||||||
if (!object_type || object_type === '') return;
|
if (!object_type || object_type === '') return;
|
||||||
|
|
||||||
var metadata = metadata_provider.get('@mo:'+object_type);
|
var metadata = metadata_provider.get('@mo:'+object_type);
|
||||||
@ -666,21 +686,31 @@ aci.attributes_widget = function(spec) {
|
|||||||
|
|
||||||
that.append = function() {
|
that.append = function() {
|
||||||
|
|
||||||
if (!that.values) return;
|
|
||||||
|
|
||||||
var unmatched = [];
|
var unmatched = [];
|
||||||
|
|
||||||
for (var i=0; i<that.values.length; i++) {
|
function add_unmatched(source) {
|
||||||
if (!that.has_option(that.values[i])) {
|
for (var i=0, l=source.length; i<l; i++) {
|
||||||
unmatched.push(that.values[i]);
|
if (!that.has_option(source[i])) {
|
||||||
|
that.add_option(source[i], true /* suppress update */);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unmatched.length > 0 && !that.skip_unmatched) {
|
add_unmatched(that.custom_options);
|
||||||
that.options.push.apply(that.options, that.prepare_options(unmatched));
|
|
||||||
|
if (that.values && !that.skip_unmatched) {
|
||||||
|
add_unmatched(that.values);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.add_custom_option = function(name, to_custom, check, update) {
|
||||||
|
|
||||||
|
var value = (name || '').toLowerCase();
|
||||||
|
if (to_custom) that.custom_options.push(value);
|
||||||
|
if (check) that.values.push(value);
|
||||||
|
if (update) that.update(that.values);
|
||||||
|
};
|
||||||
|
|
||||||
that.has_option = function(value) {
|
that.has_option = function(value) {
|
||||||
var o = that.options;
|
var o = that.options;
|
||||||
for (var i=0, l=o.length; i<l; i++) {
|
for (var i=0, l=o.length; i<l; i++) {
|
||||||
@ -689,10 +719,6 @@ aci.attributes_widget = function(spec) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
that.show_undo = function() {
|
|
||||||
$(that.undo_span).css('display', 'inline-block');
|
|
||||||
};
|
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -873,6 +899,16 @@ aci.permission_target_policy = function (spec) {
|
|||||||
var attribute_table = that.permission_target.widgets.get_widget('attrs');
|
var attribute_table = that.permission_target.widgets.get_widget('attrs');
|
||||||
var skip_unmatched_org = attribute_table.skip_unmatched;
|
var skip_unmatched_org = attribute_table.skip_unmatched;
|
||||||
attribute_table.object_type = type;
|
attribute_table.object_type = type;
|
||||||
|
|
||||||
|
// UI doesn't always know what are the possible attributes.
|
||||||
|
// In case of managed permissions, one of the possible lists is in ipapermdefaultattr.
|
||||||
|
var default_attrs = that.container.fields.get_field('ipapermdefaultattr');
|
||||||
|
if (default_attrs && default_attrs.enabled) { // if managed permission
|
||||||
|
attribute_table.custom_options = default_attrs.get_value();
|
||||||
|
} else {
|
||||||
|
attribute_table.custom_options = [];
|
||||||
|
}
|
||||||
|
|
||||||
// skip values which don't belong to new type. Bug #2617
|
// skip values which don't belong to new type. Bug #2617
|
||||||
attribute_table.skip_unmatched = skip_unmatched || skip_unmatched_org;
|
attribute_table.skip_unmatched = skip_unmatched || skip_unmatched_org;
|
||||||
attribute_field.reset();
|
attribute_field.reset();
|
||||||
@ -936,9 +972,7 @@ aci.permission_target_policy = function (spec) {
|
|||||||
var widget = that.permission_target.widgets.get_widget(target_info.name);
|
var widget = that.permission_target.widgets.get_widget(target_info.name);
|
||||||
var field = that.container.fields.get_field(target_info.name);
|
var field = that.container.fields.get_field(target_info.name);
|
||||||
that.permission_target.set_row_visible(target_info.name, visible);
|
that.permission_target.set_row_visible(target_info.name, visible);
|
||||||
var managed_f = aci.managed_fields.indexOf(target_info.name) > -1;
|
field.set_enabled(visible);
|
||||||
var enabled = !(managed_f && that.managed) && visible && !that.system;
|
|
||||||
field.set_enabled(enabled);
|
|
||||||
field.set_required(visible && target_info.required);
|
field.set_required(visible && target_info.required);
|
||||||
widget.set_visible(visible);
|
widget.set_visible(visible);
|
||||||
};
|
};
|
||||||
@ -1007,7 +1041,9 @@ aci.permission_managed_policy = function (spec) {
|
|||||||
var that = IPA.facet_policy();
|
var that = IPA.facet_policy();
|
||||||
|
|
||||||
that.post_load = function(data) {
|
that.post_load = function(data) {
|
||||||
var permtype = data.result.result.ipapermissiontype;
|
|
||||||
|
var result = data.result.result;
|
||||||
|
var permtype = result.ipapermissiontype;
|
||||||
var managed = permtype && permtype.indexOf("MANAGED") > -1;
|
var managed = permtype && permtype.indexOf("MANAGED") > -1;
|
||||||
var system = permtype && permtype.indexOf("SYSTEM") > -1 && permtype.length === 1;
|
var system = permtype && permtype.indexOf("SYSTEM") > -1 && permtype.length === 1;
|
||||||
var m_section = that.container.widgets.get_widget("managed");
|
var m_section = that.container.widgets.get_widget("managed");
|
||||||
@ -1018,7 +1054,14 @@ aci.permission_managed_policy = function (spec) {
|
|||||||
var field = fields[i];
|
var field = fields[i];
|
||||||
if (field.read_only) continue;
|
if (field.read_only) continue;
|
||||||
var managed_f = aci.managed_fields.indexOf(field.name) > -1;
|
var managed_f = aci.managed_fields.indexOf(field.name) > -1;
|
||||||
field.set_enabled(!system && !(managed_f && managed));
|
field.set_writable(!system && !(managed_f && managed) && field.writable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind rule type cannot be changed if permission is in a privilege
|
||||||
|
var privileges = result.member_privilege;
|
||||||
|
if (privileges && privileges.length > 0) {
|
||||||
|
var f = that.container.fields.get_field('ipapermbindruletype');
|
||||||
|
f.set_writable(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,12 +166,13 @@ IPA.entity_adder_dialog = function(spec) {
|
|||||||
function show_edit_page(entity,result) {
|
function show_edit_page(entity,result) {
|
||||||
var pkey_name = entity.metadata.primary_key;
|
var pkey_name = entity.metadata.primary_key;
|
||||||
var pkey = result[pkey_name];
|
var pkey = result[pkey_name];
|
||||||
if (pkey instanceof Array) {
|
if (!(pkey instanceof Array)) {
|
||||||
pkey = pkey[0];
|
pkey = [pkey];
|
||||||
}
|
}
|
||||||
|
rpc.extract_objects(pkey);
|
||||||
|
|
||||||
var pkeys = that.pkey_prefix.slice(0);
|
var pkeys = that.pkey_prefix.slice(0);
|
||||||
pkeys.push(pkey);
|
pkeys.push(pkey[0]);
|
||||||
navigation.show_entity(that.entity.name, 'default', pkeys);
|
navigation.show_entity(that.entity.name, 'default', pkeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,30 +493,24 @@ IPA.association_table_widget = function (spec) {
|
|||||||
|
|
||||||
that.table_create(container);
|
that.table_create(container);
|
||||||
|
|
||||||
that.remove_button = IPA.action_button({
|
that.remove_button = IPA.button_widget({
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
label: '@i18n:buttons.remove',
|
label: '@i18n:buttons.remove',
|
||||||
icon: 'fa-trash-o',
|
icon: 'fa-trash-o',
|
||||||
'class': 'action-button-disabled',
|
enabled: false,
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.remove_button.hasClass('action-button-disabled')) {
|
click: that.remove_handler
|
||||||
that.remove_handler();
|
});
|
||||||
}
|
that.remove_button.create(that.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.buttons);
|
|
||||||
|
|
||||||
that.add_button = IPA.action_button({
|
that.add_button = IPA.button_widget({
|
||||||
name: 'add',
|
name: 'add',
|
||||||
label: '@i18n:buttons.add',
|
label: '@i18n:buttons.add',
|
||||||
icon: 'fa-plus',
|
icon: 'fa-plus',
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.add_button.hasClass('action-button-disabled')) {
|
click: that.add_handler
|
||||||
that.add_handler();
|
});
|
||||||
}
|
that.add_button.create(that.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.buttons);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.add_handler = function() {
|
that.add_handler = function() {
|
||||||
@ -561,14 +555,13 @@ IPA.association_table_widget = function (spec) {
|
|||||||
|
|
||||||
that.set_enabled = function(enabled) {
|
that.set_enabled = function(enabled) {
|
||||||
that.table_set_enabled(enabled);
|
that.table_set_enabled(enabled);
|
||||||
if (enabled) {
|
if (!enabled) {
|
||||||
if(that.add_button) {
|
|
||||||
that.add_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$('.action-button', that.table).addClass('action-button-disabled');
|
|
||||||
that.unselect_all();
|
that.unselect_all();
|
||||||
}
|
}
|
||||||
|
if (that.add_button) {
|
||||||
|
that.add_button.set_enabled(enabled);
|
||||||
|
that.remove_button.set_enabled(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.select_changed = function() {
|
that.select_changed = function() {
|
||||||
@ -576,11 +569,7 @@ IPA.association_table_widget = function (spec) {
|
|||||||
var values = that.get_selected_values();
|
var values = that.get_selected_values();
|
||||||
|
|
||||||
if (that.remove_button) {
|
if (that.remove_button) {
|
||||||
if (values.length === 0) {
|
that.remove_button.set_enabled(values.length > 0);
|
||||||
that.remove_button.addClass('action-button-disabled');
|
|
||||||
} else {
|
|
||||||
that.remove_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1395,8 +1395,7 @@ IPA.confirm_dialog = function(spec) {
|
|||||||
name: 'ok',
|
name: 'ok',
|
||||||
label: that.ok_label,
|
label: that.ok_label,
|
||||||
click: function() {
|
click: function() {
|
||||||
that.confirmed = true;
|
that.on_confirm();
|
||||||
that.close();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1418,6 +1417,18 @@ IPA.confirm_dialog = function(spec) {
|
|||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General form dialog with confirmation feature
|
||||||
|
* @class dialog.form_dialog
|
||||||
|
* @extends {IPA.confirm_dialog}
|
||||||
|
*/
|
||||||
|
IPA.form_dialog = function(spec) {
|
||||||
|
|
||||||
|
var that = IPA.confirm_dialog(spec);
|
||||||
|
that.create_content = that.dialog_create_content;
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm mixin
|
* Confirm mixin
|
||||||
*
|
*
|
||||||
|
@ -1739,42 +1739,35 @@ IPA.dns.record_type_table_widget = function(spec) {
|
|||||||
|
|
||||||
container.addClass('dnstype-table');
|
container.addClass('dnstype-table');
|
||||||
|
|
||||||
that.remove_button = IPA.action_button({
|
that.remove_button = IPA.button_widget({
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
label: '@i18n:buttons.remove',
|
label: '@i18n:buttons.remove',
|
||||||
icon: 'fa-trash-o',
|
icon: 'fa-trash-o',
|
||||||
'class': 'action-button-disabled',
|
enabled: false,
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.remove_button.hasClass('action-button-disabled')) {
|
click: that.remove_handler
|
||||||
that.remove_handler();
|
});
|
||||||
}
|
that.remove_button.create(that.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.buttons);
|
|
||||||
|
|
||||||
that.add_button = IPA.action_button({
|
that.add_button = IPA.button_widget({
|
||||||
name: 'add',
|
name: 'add',
|
||||||
label: '@i18n:buttons.add',
|
label: '@i18n:buttons.add',
|
||||||
icon: 'fa-plus',
|
icon: 'fa-plus',
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.add_button.hasClass('action-button-disabled')) {
|
click: that.add_handler
|
||||||
that.add_handler();
|
});
|
||||||
}
|
that.add_button.create(that.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.buttons);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.set_enabled = function(enabled) {
|
that.set_enabled = function(enabled) {
|
||||||
that.table_set_enabled(enabled);
|
that.table_set_enabled(enabled);
|
||||||
if (enabled) {
|
if (!enabled) {
|
||||||
if(that.add_button) {
|
|
||||||
that.add_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$('.action-button', that.table).addClass('action-button-disabled');
|
|
||||||
that.unselect_all();
|
that.unselect_all();
|
||||||
}
|
}
|
||||||
|
if (that.add_button) {
|
||||||
|
that.add_button.set_enabled(enabled);
|
||||||
|
that.remove_button.set_enabled(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.select_changed = function() {
|
that.select_changed = function() {
|
||||||
@ -1782,11 +1775,7 @@ IPA.dns.record_type_table_widget = function(spec) {
|
|||||||
var values = that.get_selected_values();
|
var values = that.get_selected_values();
|
||||||
|
|
||||||
if (that.remove_button) {
|
if (that.remove_button) {
|
||||||
if (values.length === 0) {
|
that.remove_button.set_enabled(values.length > 0);
|
||||||
that.remove_button.addClass('action-button-disabled');
|
|
||||||
} else {
|
|
||||||
that.remove_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -672,6 +672,8 @@ exp.facet = IPA.facet = function(spec, no_init) {
|
|||||||
|
|
||||||
if (!that.dom_node) {
|
if (!that.dom_node) {
|
||||||
that.create();
|
that.create();
|
||||||
|
} else if (!that.dom_node.parentElement) {
|
||||||
|
construct.place(that.dom_node[0], that.container_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = that.state.clone();
|
var state = that.state.clone();
|
||||||
@ -728,6 +730,9 @@ exp.facet = IPA.facet = function(spec, no_init) {
|
|||||||
*/
|
*/
|
||||||
that.hide = function() {
|
that.hide = function() {
|
||||||
that.is_shown = false;
|
that.is_shown = false;
|
||||||
|
if (that.dom_node[0].parentElement) {
|
||||||
|
that.container_node.removeChild(that.dom_node[0]);
|
||||||
|
}
|
||||||
that.dom_node.removeClass('active-facet');
|
that.dom_node.removeClass('active-facet');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -285,6 +285,8 @@ define(['dojo/_base/declare',
|
|||||||
if (!this.dom_node) {
|
if (!this.dom_node) {
|
||||||
this.create();
|
this.create();
|
||||||
this.render_children();
|
this.render_children();
|
||||||
|
} else if (!this.dom_node.parentElement) {
|
||||||
|
construct.place(this.dom_node, this.container_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
dom_class.add(this.dom_node, 'active-facet');
|
dom_class.add(this.dom_node, 'active-facet');
|
||||||
@ -295,6 +297,9 @@ define(['dojo/_base/declare',
|
|||||||
* Un-mark itself as active facet
|
* Un-mark itself as active facet
|
||||||
*/
|
*/
|
||||||
hide: function() {
|
hide: function() {
|
||||||
|
if (this.dom_node.parentElement) {
|
||||||
|
this.container_node.removeChild(this.dom_node);
|
||||||
|
}
|
||||||
dom_class.remove(this.dom_node, 'active-facet');
|
dom_class.remove(this.dom_node, 'active-facet');
|
||||||
this.emit('hide', { source: this });
|
this.emit('hide', { source: this });
|
||||||
},
|
},
|
||||||
|
@ -450,6 +450,12 @@ field.field = IPA.field = function(spec) {
|
|||||||
|
|
||||||
var writable = true;
|
var writable = true;
|
||||||
|
|
||||||
|
function has_write(record, param) {
|
||||||
|
var rights = record.attributelevelrights[param];
|
||||||
|
var has = !!rights && rights.indexOf('w') > -1;
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
|
||||||
if (that.metadata) {
|
if (that.metadata) {
|
||||||
if (that.metadata.primary_key) {
|
if (that.metadata.primary_key) {
|
||||||
writable = false;
|
writable = false;
|
||||||
@ -460,21 +466,21 @@ field.field = IPA.field = function(spec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record && record.attributelevelrights) {
|
if (record && record.attributelevelrights && writable) {
|
||||||
var rights = record.attributelevelrights[that.acl_param];
|
var rights = record.attributelevelrights[that.acl_param];
|
||||||
var oc_rights= record.attributelevelrights['objectclass'];
|
var write_attr = has_write(record, that.acl_param);
|
||||||
var write_oc = oc_rights && oc_rights.indexOf('w') > -1;
|
var write_all = has_write(record, '*');
|
||||||
|
|
||||||
// Some objects in LDAP may not have set proper object class and
|
// Some objects in LDAP may not have proper object class set and
|
||||||
// therefore server doesn't send proper attribute rights. Flag
|
// therefore server doesn't send proper attribute rights. Flag
|
||||||
// 'w_if_no_aci' should be used when we want to ensure that UI
|
// 'w_if_no_aci' should be used when we want to ensure that UI
|
||||||
// shows edit interface in such cases. Usable only when user can
|
// shows edit interface in such cases. Usable only when user can
|
||||||
// modify object classes.
|
// modify object classes.
|
||||||
// For all others, lack of rights means no write.
|
var write_oc = has_write(record, 'objectclass');
|
||||||
if ((!rights && !(that.flags.indexOf('w_if_no_aci') > -1 && write_oc)) ||
|
var may_add_oc = !rights && write_oc && that.flags.indexOf('w_if_no_aci') > -1;
|
||||||
(rights && rights.indexOf('w') < 0)) {
|
|
||||||
writable = false;
|
// If no rights, change writable to False:
|
||||||
}
|
writable = write_attr || write_all || may_add_oc;
|
||||||
}
|
}
|
||||||
|
|
||||||
that.set_writable(writable);
|
that.set_writable(writable);
|
||||||
|
@ -184,12 +184,7 @@ IPA.hbac.test_facet = function(spec) {
|
|||||||
name: 'prev',
|
name: 'prev',
|
||||||
label: '@i18n:widget.prev',
|
label: '@i18n:widget.prev',
|
||||||
icon: 'fa-chevron-left',
|
icon: 'fa-chevron-left',
|
||||||
click: function() {
|
click: that.prev
|
||||||
if (!that.prev_button.hasClass('action-button-disabled')) {
|
|
||||||
that.prev();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(buttons);
|
}).appendTo(buttons);
|
||||||
|
|
||||||
buttons.append(' ');
|
buttons.append(' ');
|
||||||
@ -199,12 +194,7 @@ IPA.hbac.test_facet = function(spec) {
|
|||||||
name: 'next',
|
name: 'next',
|
||||||
label: '@i18n:widget.next',
|
label: '@i18n:widget.next',
|
||||||
icon: 'fa-chevron-right',
|
icon: 'fa-chevron-right',
|
||||||
click: function() {
|
click: that.next
|
||||||
if (!that.next_button.hasClass('action-button-disabled')) {
|
|
||||||
that.next();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(buttons);
|
}).appendTo(buttons);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -535,12 +525,7 @@ IPA.hbac.test_run_facet = function(spec) {
|
|||||||
name: 'run_test',
|
name: 'run_test',
|
||||||
label: '@i18n:objects.hbactest.run_test',
|
label: '@i18n:objects.hbactest.run_test',
|
||||||
icon: 'fa-gear',
|
icon: 'fa-gear',
|
||||||
click: function() {
|
click: that.run
|
||||||
if (!that.run_button.hasClass('action-button-disabled')) {
|
|
||||||
that.run();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(button_panel);
|
}).appendTo(button_panel);
|
||||||
|
|
||||||
var result_panel = $('<div/>', {
|
var result_panel = $('<div/>', {
|
||||||
@ -608,12 +593,7 @@ IPA.hbac.test_run_facet = function(spec) {
|
|||||||
name: 'prev',
|
name: 'prev',
|
||||||
label: '@i18n:widget.prev',
|
label: '@i18n:widget.prev',
|
||||||
icon: 'fa-chevron-left',
|
icon: 'fa-chevron-left',
|
||||||
click: function() {
|
click: that.prev
|
||||||
if (!that.prev_button.hasClass('action-button-disabled')) {
|
|
||||||
that.prev();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(buttons);
|
}).appendTo(buttons);
|
||||||
|
|
||||||
buttons.append(' ');
|
buttons.append(' ');
|
||||||
@ -622,12 +602,7 @@ IPA.hbac.test_run_facet = function(spec) {
|
|||||||
name: 'new_test',
|
name: 'new_test',
|
||||||
label: '@i18n:objects.hbactest.new_test',
|
label: '@i18n:objects.hbactest.new_test',
|
||||||
icon: 'fa-repeat',
|
icon: 'fa-repeat',
|
||||||
click: function() {
|
click: that.new_test
|
||||||
if (!that.new_test_button.hasClass('action-button-disabled')) {
|
|
||||||
that.new_test();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(buttons);
|
}).appendTo(buttons);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -710,30 +710,24 @@ IPA.sudo.options_section = function(spec) {
|
|||||||
|
|
||||||
that.table.table_create(container);
|
that.table.table_create(container);
|
||||||
|
|
||||||
that.remove_button = IPA.action_button({
|
that.remove_button = IPA.button_widget({
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
label: '@i18n:buttons.remove',
|
label: '@i18n:buttons.remove',
|
||||||
icon: 'fa-trash-o',
|
icon: 'fa-trash-o',
|
||||||
'class': 'action-button-disabled',
|
enabled: false,
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.remove_button.hasClass('action-button-disabled')) {
|
click: that.remove_handler
|
||||||
that.remove_handler();
|
});
|
||||||
}
|
that.remove_button.create(that.table.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.table.buttons);
|
|
||||||
|
|
||||||
that.add_button = IPA.action_button({
|
that.add_button = IPA.button_widget({
|
||||||
name: 'add',
|
name: 'add',
|
||||||
label: '@i18n:buttons.add',
|
label: '@i18n:buttons.add',
|
||||||
icon: 'fa-plus',
|
icon: 'fa-plus',
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.add_button.hasClass('action-button-disabled')) {
|
click: that.add_handler
|
||||||
that.add_handler();
|
});
|
||||||
}
|
that.add_button.create(that.table.buttons);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(that.table.buttons);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.table.select_changed = function() {
|
that.table.select_changed = function() {
|
||||||
@ -741,11 +735,7 @@ IPA.sudo.options_section = function(spec) {
|
|||||||
var values = that.table.get_selected_values();
|
var values = that.table.get_selected_values();
|
||||||
|
|
||||||
if (that.remove_button) {
|
if (that.remove_button) {
|
||||||
if (values.length === 0) {
|
that.remove_button.set_enabled(values.length > 0);
|
||||||
that.remove_button.addClass('action-button-disabled');
|
|
||||||
} else {
|
|
||||||
that.remove_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
define(['dojo/_base/array',
|
define(['dojo/_base/array',
|
||||||
'dojo/_base/lang',
|
'dojo/_base/lang',
|
||||||
|
'dojo/dom-construct',
|
||||||
'dojo/Evented',
|
'dojo/Evented',
|
||||||
'dojo/has',
|
'dojo/has',
|
||||||
'dojo/keys',
|
'dojo/keys',
|
||||||
@ -43,7 +44,7 @@ define(['dojo/_base/array',
|
|||||||
'./util',
|
'./util',
|
||||||
'exports'
|
'exports'
|
||||||
],
|
],
|
||||||
function(array, lang, Evented, has, keys, on, string, topic, builder,
|
function(array, lang, construct, Evented, has, keys, on, string, topic, builder,
|
||||||
datetime, entity_mod, IPA, $, navigation, phases, reg, rpc, text, util, exp) {
|
datetime, entity_mod, IPA, $, navigation, phases, reg, rpc, text, util, exp) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -568,6 +569,7 @@ IPA.input_widget = function(spec) {
|
|||||||
var changed = writable !== that.writable;
|
var changed = writable !== that.writable;
|
||||||
|
|
||||||
that.writable = writable;
|
that.writable = writable;
|
||||||
|
that.update_read_only();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
that.emit('writable-change', { source: that, writable: writable });
|
that.emit('writable-change', { source: that, writable: writable });
|
||||||
@ -584,12 +586,25 @@ IPA.input_widget = function(spec) {
|
|||||||
var changed = read_only !== that.read_only;
|
var changed = read_only !== that.read_only;
|
||||||
|
|
||||||
that.read_only = read_only;
|
that.read_only = read_only;
|
||||||
|
that.update_read_only();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
that.emit('readonly-change', { source: that, read_only: read_only });
|
that.emit('readonly-change', { source: that, read_only: read_only });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update widget's HTML based on `read_only` and `writable` properties
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
that.update_read_only = function() {
|
||||||
|
var input = that.get_input();
|
||||||
|
if (input) {
|
||||||
|
var ro = that.is_writable();
|
||||||
|
input.prop('readOnly', !ro);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Focus input element
|
* Focus input element
|
||||||
* @abstract
|
* @abstract
|
||||||
@ -635,6 +650,8 @@ IPA.input_widget = function(spec) {
|
|||||||
that.widget_set_valid = that.set_valid;
|
that.widget_set_valid = that.set_valid;
|
||||||
that.widget_hide_undo = that.hide_undo;
|
that.widget_hide_undo = that.hide_undo;
|
||||||
that.widget_show_undo = that.show_undo;
|
that.widget_show_undo = that.show_undo;
|
||||||
|
that.widget_set_writable = that.set_writable;
|
||||||
|
that.widget_set_read_only = that.set_read_only;
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
@ -736,6 +753,7 @@ IPA.text_widget = function(spec) {
|
|||||||
|
|
||||||
that.create_error_link(container);
|
that.create_error_link(container);
|
||||||
that.set_enabled(that.enabled);
|
that.set_enabled(that.enabled);
|
||||||
|
that.update_read_only();
|
||||||
that.update_input_group_state();
|
that.update_input_group_state();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -744,18 +762,23 @@ IPA.text_widget = function(spec) {
|
|||||||
*/
|
*/
|
||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
var value = values && values.length ? values[0] : '';
|
var value = values && values.length ? values[0] : '';
|
||||||
|
|
||||||
if (!that.is_writable()) {
|
|
||||||
that.display_control.text(value);
|
that.display_control.text(value);
|
||||||
|
that.input.val(value);
|
||||||
|
that.on_value_changed();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
that.update_read_only = function() {
|
||||||
|
if (!that.input) return;
|
||||||
|
if (!that.is_writable()) {
|
||||||
that.display_control.css('display', '');
|
that.display_control.css('display', '');
|
||||||
that.input_group.css('display', 'none');
|
that.input_group.css('display', 'none');
|
||||||
} else {
|
} else {
|
||||||
that.input.val(value);
|
|
||||||
that.display_control.css('display', 'none');
|
that.display_control.css('display', 'none');
|
||||||
that.input_group.css('display', '');
|
that.input_group.css('display', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
that.on_value_changed();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -811,7 +834,10 @@ IPA.text_widget = function(spec) {
|
|||||||
* visible content.
|
* visible content.
|
||||||
*/
|
*/
|
||||||
that.update_input_group_state = function() {
|
that.update_input_group_state = function() {
|
||||||
var visible = $(':visible', that.input_group_btn).length > 0;
|
var children = that.input_group_btn.children();
|
||||||
|
var visible = $.grep(children, function(el, i) {
|
||||||
|
return $(el).css('display') !== 'none';
|
||||||
|
}).length > 0;
|
||||||
that.input_group.toggleClass('input-group', visible);
|
that.input_group.toggleClass('input-group', visible);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1182,7 +1208,13 @@ IPA.multivalued_widget = function(spec) {
|
|||||||
that.on_value_changed();
|
that.on_value_changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
that.update_read_only = function() {
|
||||||
|
that.update_add_link_visibility();
|
||||||
|
};
|
||||||
|
|
||||||
that.update_add_link_visibility = function() {
|
that.update_add_link_visibility = function() {
|
||||||
|
if (!that.add_link) return;
|
||||||
var visible = that.is_writable() && that.enabled;
|
var visible = that.is_writable() && that.enabled;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
that.add_link.css('display', '');
|
that.add_link.css('display', '');
|
||||||
@ -1191,6 +1223,40 @@ IPA.multivalued_widget = function(spec) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.update_row_buttons = function(row) {
|
||||||
|
|
||||||
|
var w = that.is_writable();
|
||||||
|
if (!that.enabled || !w) {
|
||||||
|
row.widget.hide_undo();
|
||||||
|
that.toggle_remove_link(row, false);
|
||||||
|
} else {
|
||||||
|
if (row.is_new || that.test_dirty_row(row)) {
|
||||||
|
row.widget.show_undo();
|
||||||
|
that.toggle_remove_link(row, false);
|
||||||
|
} else {
|
||||||
|
that.toggle_remove_link(row, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.set_writable = function(writable) {
|
||||||
|
that.widget_set_writable(writable);
|
||||||
|
for (var i=0,l=that.rows.length; i<l; i++) {
|
||||||
|
var row = that.rows[i];
|
||||||
|
row.widget.set_writable(writable);
|
||||||
|
that.update_row_buttons(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.set_read_only = function(read_only) {
|
||||||
|
that.widget_set_read_only(read_only);
|
||||||
|
for (var i=0,l=that.rows.length; i<l; i++) {
|
||||||
|
var row = that.rows[i];
|
||||||
|
row.widget.set_read_only(read_only);
|
||||||
|
that.update_row_buttons(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
that.set_enabled = function(enabled) {
|
that.set_enabled = function(enabled) {
|
||||||
|
|
||||||
that.widget_set_enabled(enabled);
|
that.widget_set_enabled(enabled);
|
||||||
@ -1199,17 +1265,7 @@ IPA.multivalued_widget = function(spec) {
|
|||||||
for (var i=0,l=that.rows.length; i<l; i++) {
|
for (var i=0,l=that.rows.length; i<l; i++) {
|
||||||
var row = that.rows[i];
|
var row = that.rows[i];
|
||||||
row.widget.set_enabled(enabled);
|
row.widget.set_enabled(enabled);
|
||||||
|
that.update_row_buttons(row);
|
||||||
if (!enabled) {
|
|
||||||
row.widget.hide_undo();
|
|
||||||
that.toggle_remove_link(row, false);
|
|
||||||
} else {
|
|
||||||
if (row.is_new || that.test_dirty_row(row)) {
|
|
||||||
row.widget.show_undo();
|
|
||||||
} else if (that.is_writable()) {
|
|
||||||
that.toggle_remove_link(row, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1258,8 +1314,19 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
that.name = spec.name;
|
that.name = spec.name;
|
||||||
that.label = spec.label;
|
that.label = spec.label;
|
||||||
that.tooltip = spec.tooltip;
|
that.tooltip = spec.tooltip;
|
||||||
|
that.sort = spec.sort === undefined ? false : spec.sort;
|
||||||
that.value_changed = that.value_changed || IPA.observer();
|
that.value_changed = that.value_changed || IPA.observer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value which should be check when no value supplied
|
||||||
|
* @type {string|null}
|
||||||
|
*/
|
||||||
that.default_value = spec.default_value || null;
|
that.default_value = spec.default_value || null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consider empty string as non-value -> enable setting default value in such case
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
that.default_on_empty = spec.default_on_empty === undefined ? true : spec.default_on_empty;
|
that.default_on_empty = spec.default_on_empty === undefined ? true : spec.default_on_empty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1378,17 +1445,32 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.sort_options = function() {
|
||||||
|
var options = that.options.concat();
|
||||||
|
options.sort(function(a,b) {
|
||||||
|
if (a.value > b.value)
|
||||||
|
return 1;
|
||||||
|
if (a.value < b.value)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
that.create_options = function(container) {
|
that.create_options = function(container) {
|
||||||
for (var i=0; i<that.options.length; i++) {
|
container = $(container)[0];
|
||||||
|
var options = that.options;
|
||||||
|
if (that.sort) options = that.sort_options();
|
||||||
|
for (var i=0, l=options.length; i<l; i++) {
|
||||||
var option_container = that.create_option_container();
|
var option_container = that.create_option_container();
|
||||||
var option = that.options[i];
|
var option = options[i];
|
||||||
that.create_option(option, option_container);
|
that.create_option(option, option_container);
|
||||||
option_container.appendTo(container);
|
construct.place(option_container, container);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.create_option_container = function() {
|
that.create_option_container = function() {
|
||||||
return $('<li/>');
|
return construct.create('li');
|
||||||
};
|
};
|
||||||
|
|
||||||
that._create_option = function(option, container) {
|
that._create_option = function(option, container) {
|
||||||
@ -1396,11 +1478,11 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
var id = that._option_next_id + input_name;
|
var id = that._option_next_id + input_name;
|
||||||
var enabled = that.enabled && option.enabled;
|
var enabled = that.enabled && option.enabled;
|
||||||
|
|
||||||
var opt_cont = $('<span/>', {
|
var opt_cont = construct.create('span', {
|
||||||
"class": that.intput_type + '-cnt'
|
"class": that.intput_type + '-cnt'
|
||||||
}).appendTo(container);
|
});
|
||||||
|
|
||||||
option.input_node = $('<input/>', {
|
option.input_node = construct.create('input', {
|
||||||
id: id,
|
id: id,
|
||||||
type: that.input_type,
|
type: that.input_type,
|
||||||
name: input_name,
|
name: input_name,
|
||||||
@ -1408,15 +1490,16 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
value: option.value,
|
value: option.value,
|
||||||
title: option.tooltip || that.tooltip,
|
title: option.tooltip || that.tooltip,
|
||||||
change: that.on_input_change
|
change: that.on_input_change
|
||||||
}).appendTo(opt_cont);
|
}, opt_cont);
|
||||||
|
|
||||||
option.label_node = $('<label/>', {
|
option.label_node = construct.create('label', {
|
||||||
html: option.label || '',
|
|
||||||
title: option.tooltip || that.tooltip,
|
title: option.tooltip || that.tooltip,
|
||||||
'for': id
|
'for': id
|
||||||
}).appendTo(opt_cont);
|
}, opt_cont);
|
||||||
|
option.label_node.textContent = option.label || '';
|
||||||
|
|
||||||
that.new_option_id();
|
that.new_option_id();
|
||||||
|
construct.place(opt_cont, container);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.create_option = function(option, container) {
|
that.create_option = function(option, container) {
|
||||||
@ -1444,7 +1527,6 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
|
|
||||||
that.create = function(container) {
|
that.create = function(container) {
|
||||||
that.destroy();
|
that.destroy();
|
||||||
that.create_options(that.$node);
|
|
||||||
var css_class = [that.css_class, 'option_widget', that.layout,
|
var css_class = [that.css_class, 'option_widget', that.layout,
|
||||||
that.nested ? 'nested': ''].join(' ');
|
that.nested ? 'nested': ''].join(' ');
|
||||||
|
|
||||||
@ -1553,6 +1635,8 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
|
|
||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
var check = function(selector, uncheck) {
|
var check = function(selector, uncheck) {
|
||||||
$(selector, that.$node).prop('checked', !uncheck);
|
$(selector, that.$node).prop('checked', !uncheck);
|
||||||
};
|
};
|
||||||
@ -1562,23 +1646,30 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
// uncheck all inputs
|
// uncheck all inputs
|
||||||
check(that._selector, true /*uncheck*/);
|
check(that._selector, true /*uncheck*/);
|
||||||
|
|
||||||
|
// enable/disable the inputs and their children
|
||||||
|
// they might be disabled later if not checked
|
||||||
var writable = !that.read_only && !!that.writable && that.enabled;
|
var writable = !that.read_only && !!that.writable && that.enabled;
|
||||||
if (!that.nested) {
|
if (!that.nested) {
|
||||||
that.update_enabled(writable);
|
that.update_enabled(writable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values && values.length > 0) {
|
// use default value if none supplied
|
||||||
|
var def_used = false;
|
||||||
if (that.default_on_empty && that.default_value !== null) {
|
if (values && values.length > 0 && that.default_on_empty &&
|
||||||
for (var i=0; i<values.length; i++) {
|
that.default_value !== null) {
|
||||||
|
for (i=0; i<values.length; i++) {
|
||||||
if (values[i] === '') {
|
if (values[i] === '') {
|
||||||
values[i] = that.default_value;
|
values[i] = that.default_value;
|
||||||
|
def_used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (!values || !values.length) {
|
||||||
|
var default_value = that.default_value || '';
|
||||||
|
values = [default_value];
|
||||||
|
def_used = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the option when option or some of its child should be
|
// check the option if it or some of its children should be checked
|
||||||
// checked
|
|
||||||
for (i=0; i<that.options.length; i++) {
|
for (i=0; i<that.options.length; i++) {
|
||||||
var option = that.options[i];
|
var option = that.options[i];
|
||||||
var opt_vals = that.get_values(option);
|
var opt_vals = that.get_values(option);
|
||||||
@ -1589,22 +1680,14 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
if (has_opt) {
|
if (has_opt) {
|
||||||
check(that._selector+'[value="'+ option.value +'"]');
|
check(that._selector+'[value="'+ option.value +'"]');
|
||||||
}
|
}
|
||||||
if (option.widget) {
|
|
||||||
option.widget.update_enabled(writable && has_opt, false);
|
// disable options without value
|
||||||
}
|
if (option.widget && !has_opt) {
|
||||||
}
|
option.widget.update_enabled(false);
|
||||||
} else {
|
|
||||||
// select default if none specified
|
|
||||||
if (that.default_value !== null) {
|
|
||||||
check(that._selector+'[value="'+that.default_value+'"]');
|
|
||||||
// default was selected instead of supplied value, hence notify
|
|
||||||
util.emit_delayed(that, 'value-change', { source: that });
|
|
||||||
} else {
|
|
||||||
// otherwise select empty
|
|
||||||
check(that._selector+'[value=""]');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update nested
|
||||||
for (var j=0; j<that._child_widgets.length; j++) {
|
for (var j=0; j<that._child_widgets.length; j++) {
|
||||||
var widget = that._child_widgets[j];
|
var widget = that._child_widgets[j];
|
||||||
widget.writable = that.writable;
|
widget.writable = that.writable;
|
||||||
@ -1612,6 +1695,11 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
widget.enabled = that.enabled;
|
widget.enabled = that.enabled;
|
||||||
widget.update(values);
|
widget.update(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify if a value other than supplied was used
|
||||||
|
if (def_used) {
|
||||||
|
util.emit_delayed(that, 'value-change', { source: that });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (that.on_value_changed) {
|
if (that.on_value_changed) {
|
||||||
@ -1638,6 +1726,12 @@ IPA.option_widget_base = function(spec, that) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.update_read_only = function() {
|
||||||
|
// a little hack
|
||||||
|
var enabled = that.is_writable() && that.enabled;
|
||||||
|
that.update_enabled(enabled);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
that.clear = function() {
|
that.clear = function() {
|
||||||
|
|
||||||
@ -1841,6 +1935,12 @@ IPA.select_widget = function(spec) {
|
|||||||
|
|
||||||
that.widget_create(container);
|
that.widget_create(container);
|
||||||
|
|
||||||
|
that.display_control = $('<p/>', {
|
||||||
|
name: that.name,
|
||||||
|
'class': 'form-control-static',
|
||||||
|
style: 'display: none;'
|
||||||
|
}).appendTo(container);
|
||||||
|
|
||||||
that.select = $('<select/>', {
|
that.select = $('<select/>', {
|
||||||
name: that.name,
|
name: that.name,
|
||||||
'class':'form-control',
|
'class':'form-control',
|
||||||
@ -1897,10 +1997,11 @@ IPA.select_widget = function(spec) {
|
|||||||
|
|
||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
var old = that.save()[0];
|
var old = that.save()[0];
|
||||||
var value = values[0];
|
var value = values[0] || "";
|
||||||
var option = $('option[value="'+value+'"]', that.select);
|
var option = $('option[value="'+value+'"]', that.select);
|
||||||
if (option.length) {
|
if (option.length) {
|
||||||
option.prop('selected', true);
|
option.prop('selected', true);
|
||||||
|
that.display_control.text(option.text());
|
||||||
} else {
|
} else {
|
||||||
// default was selected instead of supplied value, hence notify
|
// default was selected instead of supplied value, hence notify
|
||||||
util.emit_delayed(that,'value-change', { source: that });
|
util.emit_delayed(that,'value-change', { source: that });
|
||||||
@ -1908,6 +2009,17 @@ IPA.select_widget = function(spec) {
|
|||||||
that.on_value_changed();
|
that.on_value_changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.update_read_only = function() {
|
||||||
|
if (!that.select) return;
|
||||||
|
if (!that.is_writable()) {
|
||||||
|
that.display_control.css('display', '');
|
||||||
|
that.select.css('display', 'none');
|
||||||
|
} else {
|
||||||
|
that.display_control.css('display', 'none');
|
||||||
|
that.select.css('display', '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
that.empty = function() {
|
that.empty = function() {
|
||||||
$('option', that.select).remove();
|
$('option', that.select).remove();
|
||||||
};
|
};
|
||||||
@ -2011,14 +2123,18 @@ IPA.textarea_widget = function (spec) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
var read_only = !that.is_writable();
|
|
||||||
that.input.prop('readOnly', read_only);
|
|
||||||
|
|
||||||
var value = values && values.length ? values[0] : '';
|
var value = values && values.length ? values[0] : '';
|
||||||
that.input.val(value);
|
that.input.val(value);
|
||||||
that.on_value_changed();
|
that.on_value_changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.update_read_only = function() {
|
||||||
|
if (!that.input) return;
|
||||||
|
var read_only = !that.is_writable();
|
||||||
|
that.input.prop('readOnly', read_only);
|
||||||
|
};
|
||||||
|
|
||||||
that.clear = function() {
|
that.clear = function() {
|
||||||
that.input.val('');
|
that.input.val('');
|
||||||
};
|
};
|
||||||
@ -3036,30 +3152,24 @@ IPA.attribute_table_widget = function(spec) {
|
|||||||
|
|
||||||
that.create_buttons = function(container) {
|
that.create_buttons = function(container) {
|
||||||
|
|
||||||
that.remove_button = IPA.action_button({
|
that.remove_button = IPA.button_widget({
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
label: '@i18n:buttons.remove',
|
label: '@i18n:buttons.remove',
|
||||||
icon: 'fa-trash-o',
|
icon: 'fa-trash-o',
|
||||||
'class': 'action-button-disabled',
|
enabled: false,
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.remove_button.hasClass('action-button-disabled')) {
|
click: that.remove_handler
|
||||||
that.remove_handler();
|
});
|
||||||
}
|
that.remove_button.create(container);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(container);
|
|
||||||
|
|
||||||
that.add_button = IPA.action_button({
|
that.add_button = IPA.button_widget({
|
||||||
name: 'add',
|
name: 'add',
|
||||||
label: '@i18n:buttons.add',
|
label: '@i18n:buttons.add',
|
||||||
icon: 'fa-plus',
|
icon: 'fa-plus',
|
||||||
click: function() {
|
button_class: 'btn btn-link',
|
||||||
if (!that.add_button.hasClass('action-button-disabled')) {
|
click: that.add_handler
|
||||||
that.add_handler();
|
});
|
||||||
}
|
that.add_button.create(container);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).appendTo(container);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.create = function(container) {
|
that.create = function(container) {
|
||||||
@ -3073,14 +3183,13 @@ IPA.attribute_table_widget = function(spec) {
|
|||||||
|
|
||||||
that.set_enabled = function(enabled) {
|
that.set_enabled = function(enabled) {
|
||||||
that.table_set_enabled(enabled);
|
that.table_set_enabled(enabled);
|
||||||
if (enabled) {
|
if (!enabled) {
|
||||||
if(that.add_button) {
|
|
||||||
that.add_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$('.action-button', that.table).addClass('action-button-disabled');
|
|
||||||
that.unselect_all();
|
that.unselect_all();
|
||||||
}
|
}
|
||||||
|
if (that.add_button) {
|
||||||
|
that.add_button.set_enabled(enabled);
|
||||||
|
that.remove_button.set_enabled(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.select_changed = function() {
|
that.select_changed = function() {
|
||||||
@ -3088,11 +3197,7 @@ IPA.attribute_table_widget = function(spec) {
|
|||||||
var values = that.get_selected_values();
|
var values = that.get_selected_values();
|
||||||
|
|
||||||
if (that.remove_button) {
|
if (that.remove_button) {
|
||||||
if (values.length === 0) {
|
that.remove_button.set_enabled(values.length > 0);
|
||||||
that.remove_button.addClass('action-button-disabled');
|
|
||||||
} else {
|
|
||||||
that.remove_button.removeClass('action-button-disabled');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3675,16 +3780,6 @@ IPA.combobox_widget = function(spec) {
|
|||||||
that.update = function(values) {
|
that.update = function(values) {
|
||||||
that.close();
|
that.close();
|
||||||
|
|
||||||
if (that.writable) {
|
|
||||||
that.text.css('display', 'none');
|
|
||||||
that.input.css('display', 'inline');
|
|
||||||
that.open_button.css('display', 'inline');
|
|
||||||
} else {
|
|
||||||
that.text.css('display', 'inline');
|
|
||||||
that.input.css('display', 'none');
|
|
||||||
that.open_button.css('display', 'none');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (that.searchable) {
|
if (that.searchable) {
|
||||||
that.filter.empty();
|
that.filter.empty();
|
||||||
}
|
}
|
||||||
@ -3711,6 +3806,19 @@ IPA.combobox_widget = function(spec) {
|
|||||||
that.on_value_changed();
|
that.on_value_changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.update_read_only = function() {
|
||||||
|
if (!that.input) return;
|
||||||
|
if (that.is_writable()) {
|
||||||
|
that.text.css('display', 'none');
|
||||||
|
that.input.css('display', 'inline');
|
||||||
|
that.open_button.css('display', 'inline');
|
||||||
|
} else {
|
||||||
|
that.text.css('display', 'inline');
|
||||||
|
that.input.css('display', 'none');
|
||||||
|
that.open_button.css('display', 'none');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
that.set_value = function(value) {
|
that.set_value = function(value) {
|
||||||
that.text.text(value);
|
that.text.text(value);
|
||||||
that.input.val(value);
|
that.input.val(value);
|
||||||
@ -4113,6 +4221,12 @@ IPA.button_widget = function(spec) {
|
|||||||
*/
|
*/
|
||||||
that['class'] = spec['class'];
|
that['class'] = spec['class'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override for button classes
|
||||||
|
* @property {string}
|
||||||
|
*/
|
||||||
|
that.button_class = spec.button_class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon name
|
* Icon name
|
||||||
* @property {string}
|
* @property {string}
|
||||||
@ -4126,7 +4240,7 @@ IPA.button_widget = function(spec) {
|
|||||||
*/
|
*/
|
||||||
that.on_click = function() {
|
that.on_click = function() {
|
||||||
|
|
||||||
if (that.click) {
|
if (that.click && that.enabled) {
|
||||||
that.click();
|
that.click();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -4140,6 +4254,7 @@ IPA.button_widget = function(spec) {
|
|||||||
title: that.tooltip,
|
title: that.tooltip,
|
||||||
label: that.label,
|
label: that.label,
|
||||||
'class': that['class'],
|
'class': that['class'],
|
||||||
|
button_class: that.button_class,
|
||||||
style: that.style,
|
style: that.style,
|
||||||
icon: that.icon,
|
icon: that.icon,
|
||||||
click: that.on_click
|
click: that.on_click
|
||||||
@ -4147,6 +4262,7 @@ IPA.button_widget = function(spec) {
|
|||||||
that.container = that.button;
|
that.container = that.button;
|
||||||
|
|
||||||
that.set_enabled(that.enabled);
|
that.set_enabled(that.enabled);
|
||||||
|
return that.button;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
@ -191,7 +191,7 @@ define(['dojo/_base/declare',
|
|||||||
_itemsSetter: function(value) {
|
_itemsSetter: function(value) {
|
||||||
this._clear_items();
|
this._clear_items();
|
||||||
this.items = value;
|
this.items = value;
|
||||||
this._render_items(this.items, this.dom_node);
|
this._render_items(this.items);
|
||||||
},
|
},
|
||||||
|
|
||||||
_clear_items: function() {
|
_clear_items: function() {
|
||||||
@ -201,9 +201,9 @@ define(['dojo/_base/declare',
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_render_list: function(container) {
|
_render_list: function(container, nested) {
|
||||||
|
|
||||||
var ul = this.ul_node = construct.create('ul', {
|
var ul = construct.create('ul', {
|
||||||
'class': 'dropdown-menu'
|
'class': 'dropdown-menu'
|
||||||
});
|
});
|
||||||
if (this.right_aligned) {
|
if (this.right_aligned) {
|
||||||
@ -212,14 +212,15 @@ define(['dojo/_base/declare',
|
|||||||
if (container) {
|
if (container) {
|
||||||
construct.place(ul, container);
|
construct.place(ul, container);
|
||||||
}
|
}
|
||||||
|
if (!nested) this.ul_node = ul;
|
||||||
return ul;
|
return ul;
|
||||||
},
|
},
|
||||||
|
|
||||||
_render_items: function(items, container) {
|
_render_items: function(items, container) {
|
||||||
|
|
||||||
var ul = this.ul_node;
|
if (!container) container = this.ul_node;
|
||||||
array.forEach(items, function(item) {
|
array.forEach(items, function(item) {
|
||||||
this._render_item(item, ul);
|
this._render_item(item, container);
|
||||||
}, this);
|
}, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -257,7 +258,8 @@ define(['dojo/_base/declare',
|
|||||||
|
|
||||||
if (item.items && item.items.length > 0) {
|
if (item.items && item.items.length > 0) {
|
||||||
dom_class.add(li, 'dropdown-submenu');
|
dom_class.add(li, 'dropdown-submenu');
|
||||||
this._render_items(item.items, li);
|
var ul = this._render_list(li, true);
|
||||||
|
this._render_items(item.items, ul);
|
||||||
} else {
|
} else {
|
||||||
on(a, 'click', lang.hitch(this, function(event) {
|
on(a, 'click', lang.hitch(this, function(event) {
|
||||||
this.on_item_click(event, item);
|
this.on_item_click(event, item);
|
||||||
|
@ -231,7 +231,13 @@ define(['dojo/_base/declare',
|
|||||||
|
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
if (this.buttons_node) {
|
if (this.buttons_node) {
|
||||||
this.buttons_node.innerHTML = "";
|
// detach button nodes politely
|
||||||
|
// hard methods like `innerHTML=''` might have undesired
|
||||||
|
// consequences, e.g., removal of children's content in IE
|
||||||
|
var bn = this.buttons_node;
|
||||||
|
while (bn.firstChild) {
|
||||||
|
bn.removeChild(bn.firstChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.view === 'reset') {
|
if (this.view === 'reset') {
|
||||||
this.show_reset_view();
|
this.show_reset_view();
|
||||||
|
@ -374,6 +374,9 @@
|
|||||||
"enable": "Enable token"
|
"enable": "Enable token"
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
|
"add_custom_attr": "Add custom attribute",
|
||||||
|
"attribute": "Attribute",
|
||||||
|
"filter": "Filter",
|
||||||
"identity": "Permission settings",
|
"identity": "Permission settings",
|
||||||
"managed": "Attribute breakdown",
|
"managed": "Attribute breakdown",
|
||||||
"target": "Target"
|
"target": "Target"
|
||||||
|
@ -63,3 +63,8 @@ addifnew:nsSaslMapPriority: 10
|
|||||||
# Can be removed when https://fedorahosted.org/389/ticket/47457 is fixed
|
# Can be removed when https://fedorahosted.org/389/ticket/47457 is fixed
|
||||||
dn: cn=config
|
dn: cn=config
|
||||||
only:nsslapd-sasl-max-buffer-size:2097152
|
only:nsslapd-sasl-max-buffer-size:2097152
|
||||||
|
|
||||||
|
# Allow hashed passwords to be added by non-DM users. Without this
|
||||||
|
# setting, password migration fails
|
||||||
|
dn: cn=config
|
||||||
|
only:nsslapd-allow-hashed-passwords:on
|
||||||
|
@ -11,6 +11,7 @@ default: nsAccountLock: FALSE
|
|||||||
default: ipaUniqueID: autogenerate
|
default: ipaUniqueID: autogenerate
|
||||||
|
|
||||||
dn: cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX
|
dn: cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX
|
||||||
|
add: objectClass: nestedgroup
|
||||||
default: objectClass: GroupOfNames
|
default: objectClass: GroupOfNames
|
||||||
default: objectClass: top
|
default: objectClass: top
|
||||||
default: cn: adtrust agents
|
default: cn: adtrust agents
|
||||||
|
@ -258,10 +258,10 @@ static int ipa_ldap_extended_op(LDAP *ld, const char *reqoid,
|
|||||||
int msgid;
|
int msgid;
|
||||||
int ret, rc;
|
int ret, rc;
|
||||||
|
|
||||||
ret = ldap_extended_operation(ld, KEYTAB_GET_OID, control,
|
ret = ldap_extended_operation(ld, reqoid, control,
|
||||||
NULL, NULL, &msgid);
|
NULL, NULL, &msgid);
|
||||||
if (ret != LDAP_SUCCESS) {
|
if (ret != LDAP_SUCCESS) {
|
||||||
fprintf(stderr, _("Operation failed! %s\n"), ldap_err2string(ret));
|
fprintf(stderr, _("Operation failed: %s\n"), ldap_err2string(ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,20 +270,20 @@ static int ipa_ldap_extended_op(LDAP *ld, const char *reqoid,
|
|||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
ret = ldap_result(ld, msgid, 1, &tv, &res);
|
ret = ldap_result(ld, msgid, 1, &tv, &res);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
fprintf(stderr, _("Failed to get result! %s\n"), ldap_err2string(ret));
|
fprintf(stderr, _("Failed to get result: %s\n"), ldap_err2string(ret));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0);
|
ret = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0);
|
||||||
if (ret != LDAP_SUCCESS) {
|
if (ret != LDAP_SUCCESS) {
|
||||||
fprintf(stderr, _("Failed to parse extended result! %s\n"),
|
fprintf(stderr, _("Failed to parse extended result: %s\n"),
|
||||||
ldap_err2string(ret));
|
ldap_err2string(ret));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ldap_parse_result(ld, res, &rc, NULL, &err, NULL, srvctrl, 0);
|
ret = ldap_parse_result(ld, res, &rc, NULL, &err, NULL, srvctrl, 0);
|
||||||
if (ret != LDAP_SUCCESS || rc != LDAP_SUCCESS) {
|
if (ret != LDAP_SUCCESS || rc != LDAP_SUCCESS) {
|
||||||
fprintf(stderr, _("Failed to parse result! %s\n"),
|
fprintf(stderr, _("Failed to parse result: %s\n"),
|
||||||
err ? err : ldap_err2string(ret));
|
err ? err : ldap_err2string(ret));
|
||||||
if (ret == LDAP_SUCCESS) ret = rc;
|
if (ret == LDAP_SUCCESS) ret = rc;
|
||||||
goto done;
|
goto done;
|
||||||
@ -569,7 +569,7 @@ static int ldap_get_keytab(krb5_context krbctx, bool generate, char *password,
|
|||||||
struct krb_key_salt *es = NULL;
|
struct krb_key_salt *es = NULL;
|
||||||
int num_es = 0;
|
int num_es = 0;
|
||||||
struct berval *control = NULL;
|
struct berval *control = NULL;
|
||||||
LDAP *ld;
|
LDAP *ld = NULL;
|
||||||
LDAPControl **srvctrl = NULL;
|
LDAPControl **srvctrl = NULL;
|
||||||
BerElement *ber = NULL;
|
BerElement *ber = NULL;
|
||||||
ber_tag_t rtag;
|
ber_tag_t rtag;
|
||||||
@ -917,8 +917,11 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password && (retrieve == 0) && (kvno == -1)) {
|
if (retrieve == 0 && kvno == -1) {
|
||||||
if (!quiet) fprintf(stderr, _("Retrying with old method\n"));
|
if (!quiet) {
|
||||||
|
fprintf(stderr,
|
||||||
|
_("Retrying with pre-4.0 keytab retrieval method...\n"));
|
||||||
|
}
|
||||||
|
|
||||||
/* create key material */
|
/* create key material */
|
||||||
ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys, &err_msg);
|
ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys, &err_msg);
|
||||||
@ -926,6 +929,7 @@ int main(int argc, const char *argv[])
|
|||||||
if (err_msg != NULL) {
|
if (err_msg != NULL) {
|
||||||
fprintf(stderr, "%s", err_msg);
|
fprintf(stderr, "%s", err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, _("Failed to create key material\n"));
|
fprintf(stderr, _("Failed to create key material\n"));
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
|
@ -359,7 +359,7 @@ def is_ipa_client_installed(on_master=False):
|
|||||||
return installed
|
return installed
|
||||||
|
|
||||||
def configure_nsswitch_database(fstore, database, services, preserve=True,
|
def configure_nsswitch_database(fstore, database, services, preserve=True,
|
||||||
append=True, default_value=None):
|
append=True, default_value=()):
|
||||||
"""
|
"""
|
||||||
Edits the specified nsswitch.conf database (e.g. passwd, group, sudoers)
|
Edits the specified nsswitch.conf database (e.g. passwd, group, sudoers)
|
||||||
to use the specified service(s).
|
to use the specified service(s).
|
||||||
@ -390,28 +390,34 @@ def configure_nsswitch_database(fstore, database, services, preserve=True,
|
|||||||
opts = conf.parse(f)
|
opts = conf.parse(f)
|
||||||
raw_database_entry = conf.findOpts(opts, 'option', database)[1]
|
raw_database_entry = conf.findOpts(opts, 'option', database)[1]
|
||||||
|
|
||||||
|
# Detect the list of already configured services
|
||||||
if not raw_database_entry:
|
if not raw_database_entry:
|
||||||
# If there is no database entry, database is not present in
|
# If there is no database entry, database is not present in
|
||||||
# the nsswitch.conf. Set the list of services to the
|
# the nsswitch.conf. Set the list of services to the
|
||||||
# default list, if passed.
|
# default list, if passed.
|
||||||
configured_services = ' '.join(default_value or [])
|
configured_services = list(default_value)
|
||||||
else:
|
else:
|
||||||
configured_services = raw_database_entry['value'].strip()
|
configured_services = raw_database_entry['value'].strip().split()
|
||||||
|
|
||||||
|
# Make sure no service is added if already mentioned in the list
|
||||||
|
added_services = [s for s in services
|
||||||
|
if s not in configured_services]
|
||||||
|
|
||||||
|
# Prepend / append the list of new services
|
||||||
if append:
|
if append:
|
||||||
new_services = ' ' + configured_services + ' ' + ' '.join(services)
|
new_value = ' ' + ' '.join(configured_services + added_services)
|
||||||
else:
|
else:
|
||||||
new_services = ' ' + ' '.join(services) + ' ' + configured_services
|
new_value = ' ' + ' '.join(added_services + configured_services)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Preserve not set, let's rewrite existing configuration
|
# Preserve not set, let's rewrite existing configuration
|
||||||
new_services = ' ' + ' '.join(services)
|
new_value = ' ' + ' '.join(services)
|
||||||
|
|
||||||
# Set new services as sources for database
|
# Set new services as sources for database
|
||||||
opts = [{'name': database,
|
opts = [{'name': database,
|
||||||
'type':'option',
|
'type':'option',
|
||||||
'action':'set',
|
'action':'set',
|
||||||
'value': new_services
|
'value': new_value
|
||||||
},
|
},
|
||||||
{'name':'empty',
|
{'name':'empty',
|
||||||
'type':'empty'
|
'type':'empty'
|
||||||
@ -491,7 +497,8 @@ def uninstall(options, env):
|
|||||||
"Failed to remove IPA CA from /etc/pki/nssdb: %s", str(e))
|
"Failed to remove IPA CA from /etc/pki/nssdb: %s", str(e))
|
||||||
|
|
||||||
# Always start certmonger. We can't untrack something if it isn't
|
# Always start certmonger. We can't untrack something if it isn't
|
||||||
# running
|
# running. Note that this is legacy code to untrack any certificates
|
||||||
|
# that were created by previous versions of this installer.
|
||||||
messagebus = services.knownservices.messagebus
|
messagebus = services.knownservices.messagebus
|
||||||
try:
|
try:
|
||||||
messagebus.start()
|
messagebus.start()
|
||||||
@ -506,7 +513,7 @@ def uninstall(options, env):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
certmonger.stop_tracking(paths.NSS_DB_DIR, nickname=client_nss_nickname)
|
certmonger.stop_tracking(paths.NSS_DB_DIR, nickname=client_nss_nickname)
|
||||||
except (CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error("%s failed to stop tracking certificate: %s",
|
root_logger.error("%s failed to stop tracking certificate: %s",
|
||||||
cmonger.service_name, str(e))
|
cmonger.service_name, str(e))
|
||||||
|
|
||||||
@ -1065,69 +1072,6 @@ def configure_krb5_conf(cli_realm, cli_domain, cli_server, cli_kdc, dnsok,
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def configure_certmonger(fstore, subject_base, cli_realm, hostname, options,
|
|
||||||
remote_env):
|
|
||||||
started = True
|
|
||||||
principal = 'host/%s@%s' % (hostname, cli_realm)
|
|
||||||
|
|
||||||
messagebus = services.knownservices.messagebus
|
|
||||||
try:
|
|
||||||
messagebus.start()
|
|
||||||
except Exception, e:
|
|
||||||
log_service_error(messagebus.service_name, 'start', e)
|
|
||||||
|
|
||||||
# Ensure that certmonger has been started at least once to generate the
|
|
||||||
# cas files in /var/lib/certmonger/cas.
|
|
||||||
cmonger = services.knownservices.certmonger
|
|
||||||
try:
|
|
||||||
cmonger.restart()
|
|
||||||
except Exception, e:
|
|
||||||
log_service_error(cmonger.service_name, 'restart', e)
|
|
||||||
|
|
||||||
if options.hostname:
|
|
||||||
# It needs to be stopped if we touch them
|
|
||||||
try:
|
|
||||||
cmonger.stop()
|
|
||||||
except Exception, e:
|
|
||||||
log_service_error(cmonger.service_name, 'stop', e)
|
|
||||||
# If the hostname is explicitly set then we need to tell certmonger
|
|
||||||
# which principal name to use when requesting certs.
|
|
||||||
certmonger.add_principal_to_cas(principal)
|
|
||||||
|
|
||||||
try:
|
|
||||||
cmonger.restart()
|
|
||||||
except Exception, e:
|
|
||||||
log_service_error(cmonger.service_name, 'restart', e)
|
|
||||||
root_logger.warning(
|
|
||||||
"Automatic certificate management will not be available")
|
|
||||||
started = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
cmonger.enable()
|
|
||||||
except Exception, e:
|
|
||||||
root_logger.error(
|
|
||||||
"Failed to configure automatic startup of the %s daemon: %s",
|
|
||||||
cmonger.service_name, str(e))
|
|
||||||
root_logger.warning(
|
|
||||||
"Automatic certificate management will not be available")
|
|
||||||
|
|
||||||
# Request our host cert
|
|
||||||
if remote_env['enable_ra']:
|
|
||||||
if started:
|
|
||||||
client_nss_nickname = client_nss_nickname_format % hostname
|
|
||||||
subject = DN(('CN', hostname), subject_base)
|
|
||||||
try:
|
|
||||||
run(["ipa-getcert", "request", "-d", paths.NSS_DB_DIR,
|
|
||||||
"-n", client_nss_nickname, "-N", str(subject),
|
|
||||||
"-K", principal])
|
|
||||||
except Exception:
|
|
||||||
root_logger.error("%s request for host certificate failed",
|
|
||||||
cmonger.service_name)
|
|
||||||
else:
|
|
||||||
root_logger.warning(
|
|
||||||
"A RA is not configured on the server. "
|
|
||||||
"Not requesting host certificate.")
|
|
||||||
|
|
||||||
def configure_sssd_conf(fstore, cli_realm, cli_domain, cli_server, options, client_domain, client_hostname):
|
def configure_sssd_conf(fstore, cli_realm, cli_domain, cli_server, options, client_domain, client_hostname):
|
||||||
try:
|
try:
|
||||||
sssdconfig = SSSDConfig.SSSDConfig()
|
sssdconfig = SSSDConfig.SSSDConfig()
|
||||||
@ -2119,7 +2063,7 @@ def install(options, env, fstore, statestore):
|
|||||||
# Create the discovery instance
|
# Create the discovery instance
|
||||||
ds = ipadiscovery.IPADiscovery()
|
ds = ipadiscovery.IPADiscovery()
|
||||||
|
|
||||||
ret = ds.search(domain=options.domain, servers=options.server, hostname=hostname, ca_cert_path=get_cert_path(options.ca_cert_file))
|
ret = ds.search(domain=options.domain, servers=options.server, realm=options.realm_name, hostname=hostname, ca_cert_path=get_cert_path(options.ca_cert_file))
|
||||||
|
|
||||||
if options.server and ret != 0:
|
if options.server and ret != 0:
|
||||||
# There is no point to continue with installation as server list was
|
# There is no point to continue with installation as server list was
|
||||||
@ -2635,8 +2579,6 @@ def install(options, env, fstore, statestore):
|
|||||||
|
|
||||||
if not options.on_master:
|
if not options.on_master:
|
||||||
client_dns(cli_server[0], hostname, options.dns_updates)
|
client_dns(cli_server[0], hostname, options.dns_updates)
|
||||||
configure_certmonger(fstore, subject_base, cli_realm, hostname,
|
|
||||||
options, remote_env)
|
|
||||||
|
|
||||||
update_ssh_keys(cli_server[0], hostname, services.knownservices.sshd.get_config_dir(), options.create_sshfp)
|
update_ssh_keys(cli_server[0], hostname, services.knownservices.sshd.get_config_dir(), options.create_sshfp)
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class IPADiscovery(object):
|
|||||||
domain = domain[p+1:]
|
domain = domain[p+1:]
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
def search(self, domain = "", servers = "", hostname=None, ca_cert_path=None):
|
def search(self, domain="", servers="", realm=None, hostname=None, ca_cert_path=None):
|
||||||
"""
|
"""
|
||||||
Use DNS discovery to identify valid IPA servers.
|
Use DNS discovery to identify valid IPA servers.
|
||||||
|
|
||||||
@ -218,13 +218,21 @@ class IPADiscovery(object):
|
|||||||
|
|
||||||
#search for kerberos
|
#search for kerberos
|
||||||
root_logger.debug("[Kerberos realm search]")
|
root_logger.debug("[Kerberos realm search]")
|
||||||
krb_realm, kdc = self.ipadnssearchkrb(self.domain)
|
if realm:
|
||||||
if not servers and not krb_realm:
|
root_logger.debug("Kerberos realm forced")
|
||||||
|
self.realm = realm
|
||||||
|
self.realm_source = 'Forced'
|
||||||
|
else:
|
||||||
|
realm = self.ipadnssearchkrbrealm()
|
||||||
|
self.realm = realm
|
||||||
|
self.realm_source = (
|
||||||
|
'Discovered Kerberos DNS records from %s' % self.domain)
|
||||||
|
|
||||||
|
if not servers and not realm:
|
||||||
return REALM_NOT_FOUND
|
return REALM_NOT_FOUND
|
||||||
|
|
||||||
self.realm = krb_realm
|
self.kdc = self.ipadnssearchkrbkdc()
|
||||||
self.kdc = kdc
|
self.kdc_source = (
|
||||||
self.realm_source = self.kdc_source = (
|
|
||||||
'Discovered Kerberos DNS records from %s' % self.domain)
|
'Discovered Kerberos DNS records from %s' % self.domain)
|
||||||
|
|
||||||
# We may have received multiple servers corresponding to the domain
|
# We may have received multiple servers corresponding to the domain
|
||||||
@ -335,6 +343,10 @@ class IPADiscovery(object):
|
|||||||
no_schema=True, decode_attrs=False)
|
no_schema=True, decode_attrs=False)
|
||||||
try:
|
try:
|
||||||
lh.do_simple_bind(DN(), '')
|
lh.do_simple_bind(DN(), '')
|
||||||
|
|
||||||
|
# get IPA base DN
|
||||||
|
root_logger.debug("Search LDAP server for IPA base DN")
|
||||||
|
basedn = get_ipa_basedn(lh)
|
||||||
except errors.ACIError:
|
except errors.ACIError:
|
||||||
root_logger.debug("LDAP Error: Anonymous access not allowed")
|
root_logger.debug("LDAP Error: Anonymous access not allowed")
|
||||||
return [NO_ACCESS_TO_LDAP]
|
return [NO_ACCESS_TO_LDAP]
|
||||||
@ -350,10 +362,6 @@ class IPADiscovery(object):
|
|||||||
else:
|
else:
|
||||||
return [UNKNOWN_ERROR]
|
return [UNKNOWN_ERROR]
|
||||||
|
|
||||||
# get IPA base DN
|
|
||||||
root_logger.debug("Search LDAP server for IPA base DN")
|
|
||||||
basedn = get_ipa_basedn(lh)
|
|
||||||
|
|
||||||
if basedn is None:
|
if basedn is None:
|
||||||
root_logger.debug("The server is not an IPA server")
|
root_logger.debug("The server is not an IPA server")
|
||||||
return [NOT_IPA_SERVER]
|
return [NOT_IPA_SERVER]
|
||||||
@ -452,11 +460,12 @@ class IPADiscovery(object):
|
|||||||
|
|
||||||
return servers
|
return servers
|
||||||
|
|
||||||
def ipadnssearchkrb(self, tdomain):
|
def ipadnssearchkrbrealm(self, domain=None):
|
||||||
realm = None
|
realm = None
|
||||||
kdc = None
|
if not domain:
|
||||||
|
domain = self.domain
|
||||||
# now, check for a Kerberos realm the local host or domain is in
|
# now, check for a Kerberos realm the local host or domain is in
|
||||||
qname = "_kerberos." + tdomain
|
qname = "_kerberos." + domain
|
||||||
|
|
||||||
root_logger.debug("Search DNS for TXT record of %s", qname)
|
root_logger.debug("Search DNS for TXT record of %s", qname)
|
||||||
|
|
||||||
@ -472,10 +481,13 @@ class IPADiscovery(object):
|
|||||||
realm = answer.strings[0]
|
realm = answer.strings[0]
|
||||||
if realm:
|
if realm:
|
||||||
break
|
break
|
||||||
|
return realm
|
||||||
|
|
||||||
if realm:
|
def ipadnssearchkrbkdc(self, domain=None):
|
||||||
# now fetch server information for the realm
|
kdc = None
|
||||||
domain = realm.lower()
|
|
||||||
|
if not domain:
|
||||||
|
domain = self.domain
|
||||||
|
|
||||||
kdc = self.ipadns_search_srv(domain, '_kerberos._udp', 88,
|
kdc = self.ipadns_search_srv(domain, '_kerberos._udp', 88,
|
||||||
break_on_first=False)
|
break_on_first=False)
|
||||||
@ -483,7 +495,7 @@ class IPADiscovery(object):
|
|||||||
if kdc:
|
if kdc:
|
||||||
kdc = ','.join(kdc)
|
kdc = ','.join(kdc)
|
||||||
else:
|
else:
|
||||||
root_logger.debug("SRV record for KDC not found! Realm: %s, SRV record: %s" % (realm, qname))
|
root_logger.debug("SRV record for KDC not found! Domain: %s" % domain)
|
||||||
kdc = None
|
kdc = None
|
||||||
|
|
||||||
return realm, kdc
|
return kdc
|
||||||
|
@ -584,6 +584,12 @@ class InvalidSessionPassword(SessionError):
|
|||||||
errno = 1201
|
errno = 1201
|
||||||
format= _('Principal %(principal)s cannot be authenticated: %(message)s')
|
format= _('Principal %(principal)s cannot be authenticated: %(message)s')
|
||||||
|
|
||||||
|
class PasswordExpired(InvalidSessionPassword):
|
||||||
|
"""
|
||||||
|
**1202** Raised when we cannot obtain a TGT for a principal because the password is expired.
|
||||||
|
"""
|
||||||
|
errno = 1202
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# 2000 - 2999: Authorization errors
|
# 2000 - 2999: Authorization errors
|
||||||
class AuthorizationError(PublicError):
|
class AuthorizationError(PublicError):
|
||||||
@ -811,6 +817,22 @@ class DeprecationError(InvocationError):
|
|||||||
errno = 3015
|
errno = 3015
|
||||||
format = _("Command '%(name)s' has been deprecated")
|
format = _("Command '%(name)s' has been deprecated")
|
||||||
|
|
||||||
|
class NotAForestRootError(InvocationError):
|
||||||
|
"""
|
||||||
|
**3016** Raised when an attempt to establish trust is done against non-root domain
|
||||||
|
Forest root domain has the same name as the forest itself
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
>>> raise NotAForestRootError(forest='example.test', domain='jointops.test')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NotAForestRootError: Domain 'jointops.test' is not a root domain for forest 'example.test'
|
||||||
|
"""
|
||||||
|
|
||||||
|
errno = 3016
|
||||||
|
format = _("Domain '%(domain)s' is not a root domain for forest '%(forest)s'")
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# 4000 - 4999: Execution errors
|
# 4000 - 4999: Execution errors
|
||||||
@ -1113,19 +1135,19 @@ class DefaultGroupError(ExecutionError):
|
|||||||
|
|
||||||
class DNSNotARecordError(ExecutionError):
|
class DNSNotARecordError(ExecutionError):
|
||||||
"""
|
"""
|
||||||
**4019** Raised when a hostname is not a DNS A record
|
**4019** Raised when a hostname is not a DNS A/AAAA record
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
>>> raise DNSNotARecordError()
|
>>> raise DNSNotARecordError()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
DNSNotARecordError: Host does not have corresponding DNS A record
|
DNSNotARecordError: Host does not have corresponding DNS A/AAAA record
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
errno = 4019
|
errno = 4019
|
||||||
format = _('Host does not have corresponding DNS A record')
|
format = _('Host does not have corresponding DNS A/AAAA record')
|
||||||
|
|
||||||
class ManagedGroupError(ExecutionError):
|
class ManagedGroupError(ExecutionError):
|
||||||
"""
|
"""
|
||||||
|
@ -210,6 +210,10 @@ def get_effective_rights(ldap, dn, attrs=None):
|
|||||||
rights = rights[0].split(', ')
|
rights = rights[0].split(', ')
|
||||||
for r in rights:
|
for r in rights:
|
||||||
(k,v) = r.split(':')
|
(k,v) = r.split(':')
|
||||||
|
if v == 'none':
|
||||||
|
# the string "none" means "no rights found"
|
||||||
|
# see https://fedorahosted.org/freeipa/ticket/4359
|
||||||
|
v = u''
|
||||||
rdict[k.strip().lower()] = v
|
rdict[k.strip().lower()] = v
|
||||||
|
|
||||||
return rdict
|
return rdict
|
||||||
@ -236,9 +240,13 @@ def entry_from_entry(entry, newentry):
|
|||||||
def entry_to_dict(entry, **options):
|
def entry_to_dict(entry, **options):
|
||||||
if options.get('raw', False):
|
if options.get('raw', False):
|
||||||
result = {}
|
result = {}
|
||||||
for attr, value in entry.raw.iteritems():
|
for attr in entry.iterkeys():
|
||||||
if entry.conn.get_type(attr) is not str:
|
if attr.lower() == 'attributelevelrights':
|
||||||
value = list(value)
|
value = entry[attr]
|
||||||
|
elif entry.conn.get_type(attr) is str:
|
||||||
|
value = entry.raw[attr]
|
||||||
|
else:
|
||||||
|
value = list(entry.raw[attr])
|
||||||
for (i, v) in enumerate(value):
|
for (i, v) in enumerate(value):
|
||||||
try:
|
try:
|
||||||
value[i] = v.decode('utf-8')
|
value[i] = v.decode('utf-8')
|
||||||
|
@ -209,11 +209,11 @@ EXAMPLES:
|
|||||||
authoritative (e.g. sub.example.com) will be routed to the global forwarder.
|
authoritative (e.g. sub.example.com) will be routed to the global forwarder.
|
||||||
Global forwarding configuration can be overridden per-zone.
|
Global forwarding configuration can be overridden per-zone.
|
||||||
""") + _("""
|
""") + _("""
|
||||||
Semantics of forwarding in IPA matches BIND sematics and depends on type
|
Semantics of forwarding in IPA matches BIND semantics and depends on the type
|
||||||
of the zone:
|
of zone:
|
||||||
* Master zone: local BIND replies authoritatively to queries for data in
|
* Master zone: local BIND replies authoritatively to queries for data in
|
||||||
the given zone (including authoritative NXDOMAIN answers) and forwarding
|
the given zone (including authoritative NXDOMAIN answers) and forwarding
|
||||||
affects only queries for names bellow zone cuts (NS records) of locally
|
affects only queries for names below zone cuts (NS records) of locally
|
||||||
served zones.
|
served zones.
|
||||||
|
|
||||||
* Forward zone: forward zone contains no authoritative data. BIND forwards
|
* Forward zone: forward zone contains no authoritative data. BIND forwards
|
||||||
@ -489,6 +489,14 @@ def _hostname_validator(ugettext, value):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _no_wildcard_validator(ugettext, value):
|
||||||
|
"""Disallow usage of wildcards as RFC 4592 section 4 recommends
|
||||||
|
"""
|
||||||
|
assert isinstance(value, DNSName)
|
||||||
|
if value.is_wild():
|
||||||
|
return _('should not be a wildcard domain name (RFC 4592 section 4)')
|
||||||
|
return None
|
||||||
|
|
||||||
def is_forward_record(zone, str_address):
|
def is_forward_record(zone, str_address):
|
||||||
addr = netaddr.IPAddress(str_address)
|
addr = netaddr.IPAddress(str_address)
|
||||||
if addr.version == 4:
|
if addr.version == 4:
|
||||||
@ -1731,6 +1739,7 @@ class DNSZoneBase(LDAPObject):
|
|||||||
|
|
||||||
takes_params = (
|
takes_params = (
|
||||||
DNSNameParam('idnsname',
|
DNSNameParam('idnsname',
|
||||||
|
_no_wildcard_validator, # RFC 4592 section 4
|
||||||
only_absolute=True,
|
only_absolute=True,
|
||||||
cli_name='name',
|
cli_name='name',
|
||||||
label=_('Zone name'),
|
label=_('Zone name'),
|
||||||
@ -2619,6 +2628,19 @@ class dnsrecord(LDAPObject):
|
|||||||
error=unicode(_('out-of-zone data: record name must '
|
error=unicode(_('out-of-zone data: record name must '
|
||||||
'be a subdomain of the zone or a '
|
'be a subdomain of the zone or a '
|
||||||
'relative name')))
|
'relative name')))
|
||||||
|
# dissallowed wildcard (RFC 4592 section 4)
|
||||||
|
no_wildcard_rtypes = ['DNAME', 'DS', 'NS']
|
||||||
|
if (keys[-1].is_wild() and
|
||||||
|
any(entry_attrs.get('%srecord' % r.lower())
|
||||||
|
for r in no_wildcard_rtypes)
|
||||||
|
):
|
||||||
|
raise errors.ValidationError(
|
||||||
|
name='idnsname',
|
||||||
|
error=(_('owner of %(types)s records '
|
||||||
|
'should not be a wildcard domain name (RFC 4592 section 4)') %
|
||||||
|
{'types': ', '.join(no_wildcard_rtypes)}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
assert isinstance(dn, DN)
|
assert isinstance(dn, DN)
|
||||||
@ -3374,6 +3396,13 @@ class dnsrecord_mod(LDAPUpdate):
|
|||||||
if del_all:
|
if del_all:
|
||||||
result = self.obj.methods.delentry(*keys,
|
result = self.obj.methods.delentry(*keys,
|
||||||
version=options['version'])
|
version=options['version'])
|
||||||
|
|
||||||
|
# we need to modify delete result to match mod output type
|
||||||
|
# only one value is expected, not a list
|
||||||
|
if client_has_capability(options['version'], 'primary_key_types'):
|
||||||
|
assert len(result['value']) == 1
|
||||||
|
result['value'] = result['value'][0]
|
||||||
|
|
||||||
# indicate that entry was deleted
|
# indicate that entry was deleted
|
||||||
context.dnsrecord_entry_mods[(keys[0], keys[1])] = None
|
context.dnsrecord_entry_mods[(keys[0], keys[1])] = None
|
||||||
|
|
||||||
|
@ -202,6 +202,16 @@ class group(LDAPObject):
|
|||||||
],
|
],
|
||||||
'default_privileges': {'Group Administrators'},
|
'default_privileges': {'Group Administrators'},
|
||||||
},
|
},
|
||||||
|
'System: Read Group Compat Tree': {
|
||||||
|
'non_object': True,
|
||||||
|
'ipapermbindruletype': 'anonymous',
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtarget': DN('cn=groups', 'cn=compat', api.env.basedn),
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'objectclass', 'cn', 'memberuid', 'gidnumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
label = _('User Groups')
|
label = _('User Groups')
|
||||||
@ -522,7 +532,7 @@ class group_remove_member(LDAPRemoveMember):
|
|||||||
|
|
||||||
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
|
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
|
||||||
assert isinstance(dn, DN)
|
assert isinstance(dn, DN)
|
||||||
if keys[0] in PROTECTED_GROUPS:
|
if keys[0] in PROTECTED_GROUPS and 'user' in options:
|
||||||
protected_group_name = keys[0]
|
protected_group_name = keys[0]
|
||||||
result = api.Command.group_show(protected_group_name)
|
result = api.Command.group_show(protected_group_name)
|
||||||
users_left = set(result['result'].get('member_user', []))
|
users_left = set(result['result'].get('member_user', []))
|
||||||
|
@ -368,6 +368,16 @@ class host(LDAPObject):
|
|||||||
'ipapermdefaultattr': {'userpassword'},
|
'ipapermdefaultattr': {'userpassword'},
|
||||||
'default_privileges': {'Host Administrators', 'Host Enrollment'},
|
'default_privileges': {'Host Administrators', 'Host Enrollment'},
|
||||||
},
|
},
|
||||||
|
'System: Read Host Compat Tree': {
|
||||||
|
'non_object': True,
|
||||||
|
'ipapermbindruletype': 'anonymous',
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtarget': DN('cn=computers', 'cn=compat', api.env.basedn),
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'objectclass', 'cn', 'macaddress',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
label = _('Hosts')
|
label = _('Hosts')
|
||||||
|
@ -185,13 +185,14 @@ class idrange(LDAPObject):
|
|||||||
label = _('ID Ranges')
|
label = _('ID Ranges')
|
||||||
label_singular = _('ID Range')
|
label_singular = _('ID Range')
|
||||||
|
|
||||||
|
# The commented range types are planned but not yet supported
|
||||||
range_types = {
|
range_types = {
|
||||||
u'ipa-local': unicode(_('local domain range')),
|
u'ipa-local': unicode(_('local domain range')),
|
||||||
u'ipa-ad-winsync': unicode(_('Active Directory winsync range')),
|
# u'ipa-ad-winsync': unicode(_('Active Directory winsync range')),
|
||||||
u'ipa-ad-trust': unicode(_('Active Directory domain range')),
|
u'ipa-ad-trust': unicode(_('Active Directory domain range')),
|
||||||
u'ipa-ad-trust-posix': unicode(_('Active Directory trust range with '
|
u'ipa-ad-trust-posix': unicode(_('Active Directory trust range with '
|
||||||
'POSIX attributes')),
|
'POSIX attributes')),
|
||||||
u'ipa-ipa-trust': unicode(_('IPA trust range')),
|
# u'ipa-ipa-trust': unicode(_('IPA trust range')),
|
||||||
}
|
}
|
||||||
|
|
||||||
takes_params = (
|
takes_params = (
|
||||||
|
@ -518,6 +518,9 @@ class i18n_messages(Command):
|
|||||||
"enable": "Enable token",
|
"enable": "Enable token",
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
|
"add_custom_attr": _("Add custom attribute"),
|
||||||
|
"attribute": _("Attribute"),
|
||||||
|
"filter": _("Filter"),
|
||||||
"identity": _("Permission settings"),
|
"identity": _("Permission settings"),
|
||||||
"managed": _("Attribute breakdown"),
|
"managed": _("Attribute breakdown"),
|
||||||
"target": _("Target"),
|
"target": _("Target"),
|
||||||
|
@ -160,6 +160,16 @@ class netgroup(LDAPObject):
|
|||||||
],
|
],
|
||||||
'default_privileges': {'Netgroups Administrators'},
|
'default_privileges': {'Netgroups Administrators'},
|
||||||
},
|
},
|
||||||
|
'System: Read Netgroup Compat Tree': {
|
||||||
|
'non_object': True,
|
||||||
|
'ipapermbindruletype': 'anonymous',
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtarget': DN('cn=ng', 'cn=compat', api.env.basedn),
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'objectclass', 'cn', 'membernisnetgroup', 'nisnetgrouptriple',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
label = _('Netgroups')
|
label = _('Netgroups')
|
||||||
|
@ -21,7 +21,7 @@ from ipalib.plugins.baseldap import DN, LDAPObject, LDAPAddMember, LDAPRemoveMem
|
|||||||
from ipalib.plugins.baseldap import LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, LDAPRetrieve
|
from ipalib.plugins.baseldap import LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, LDAPRetrieve
|
||||||
from ipalib import api, Int, Str, Bool, DateTime, Flag, Bytes, IntEnum, StrEnum, Password, _, ngettext
|
from ipalib import api, Int, Str, Bool, DateTime, Flag, Bytes, IntEnum, StrEnum, Password, _, ngettext
|
||||||
from ipalib.plugable import Registry
|
from ipalib.plugable import Registry
|
||||||
from ipalib.errors import PasswordMismatch, ConversionError, LastMemberError, NotFound
|
from ipalib.errors import PasswordMismatch, ConversionError, LastMemberError, NotFound, ValidationError
|
||||||
from ipalib.request import context
|
from ipalib.request import context
|
||||||
from ipalib.frontend import Local
|
from ipalib.frontend import Local
|
||||||
|
|
||||||
@ -103,6 +103,11 @@ def _normalize_owner(userobj, entry_attrs):
|
|||||||
if owner is not None:
|
if owner is not None:
|
||||||
entry_attrs['ipatokenowner'] = userobj.get_dn(owner)
|
entry_attrs['ipatokenowner'] = userobj.get_dn(owner)
|
||||||
|
|
||||||
|
def _check_interval(not_before, not_after):
|
||||||
|
if not_before and not_after:
|
||||||
|
return not_before <= not_after
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
class otptoken(LDAPObject):
|
class otptoken(LDAPObject):
|
||||||
@ -254,6 +259,11 @@ class otptoken_add(LDAPCreate):
|
|||||||
entry_attrs['ipatokenuniqueid'] = str(uuid.uuid4())
|
entry_attrs['ipatokenuniqueid'] = str(uuid.uuid4())
|
||||||
dn = DN("ipatokenuniqueid=%s" % entry_attrs['ipatokenuniqueid'], dn)
|
dn = DN("ipatokenuniqueid=%s" % entry_attrs['ipatokenuniqueid'], dn)
|
||||||
|
|
||||||
|
if not _check_interval(options.get('ipatokennotbefore', None),
|
||||||
|
options.get('ipatokennotafter', None)):
|
||||||
|
raise ValidationError(name='not_after',
|
||||||
|
error='is before the validity start')
|
||||||
|
|
||||||
# Set the object class and defaults for specific token types
|
# Set the object class and defaults for specific token types
|
||||||
entry_attrs['objectclass'] = otptoken.object_class + ['ipatoken' + options['type']]
|
entry_attrs['objectclass'] = otptoken.object_class + ['ipatoken' + options['type']]
|
||||||
for ttype, tattrs in TOKEN_TYPES.items():
|
for ttype, tattrs in TOKEN_TYPES.items():
|
||||||
@ -336,6 +346,25 @@ class otptoken_mod(LDAPUpdate):
|
|||||||
msg_summary = _('Modified OTP token "%(value)s"')
|
msg_summary = _('Modified OTP token "%(value)s"')
|
||||||
|
|
||||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||||
|
notafter_set = True
|
||||||
|
notbefore = options.get('ipatokennotbefore', None)
|
||||||
|
notafter = options.get('ipatokennotafter', None)
|
||||||
|
# notbefore xor notafter, exactly one of them is not None
|
||||||
|
if bool(notbefore) ^ bool(notafter):
|
||||||
|
result = self.api.Command.otptoken_show(keys[-1])['result']
|
||||||
|
if notbefore is None:
|
||||||
|
notbefore = result.get('ipatokennotbefore', [None])[0]
|
||||||
|
if notafter is None:
|
||||||
|
notafter_set = False
|
||||||
|
notafter = result.get('ipatokennotafter', [None])[0]
|
||||||
|
|
||||||
|
if not _check_interval(notbefore, notafter):
|
||||||
|
if notafter_set:
|
||||||
|
raise ValidationError(name='not_after',
|
||||||
|
error='is before the validity start')
|
||||||
|
else:
|
||||||
|
raise ValidationError(name='not_before',
|
||||||
|
error='is after the validity end')
|
||||||
_normalize_owner(self.api.Object.user, entry_attrs)
|
_normalize_owner(self.api.Object.user, entry_attrs)
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
|
@ -296,8 +296,9 @@ class permission(baseldap.LDAPObject):
|
|||||||
DNParam(
|
DNParam(
|
||||||
'ipapermtarget?',
|
'ipapermtarget?',
|
||||||
cli_name='target',
|
cli_name='target',
|
||||||
label=_('ACI target DN'),
|
label=_('Target DN'),
|
||||||
flags={'no_option'}
|
doc=_('Optional DN to apply the permission to '
|
||||||
|
'(must be in the subtree, but may not yet exist)'),
|
||||||
),
|
),
|
||||||
|
|
||||||
Str('memberof*',
|
Str('memberof*',
|
||||||
|
@ -166,7 +166,7 @@ class sudorule(LDAPObject):
|
|||||||
'non_object': True,
|
'non_object': True,
|
||||||
'ipapermlocation': api.env.basedn,
|
'ipapermlocation': api.env.basedn,
|
||||||
'ipapermtarget': DN('ou=sudoers', api.env.basedn),
|
'ipapermtarget': DN('ou=sudoers', api.env.basedn),
|
||||||
'ipapermbindruletype': 'all',
|
'ipapermbindruletype': 'anonymous',
|
||||||
'ipapermright': {'read', 'search', 'compare'},
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
'ipapermdefaultattr': {
|
'ipapermdefaultattr': {
|
||||||
'objectclass', 'cn', 'ou',
|
'objectclass', 'cn', 'ou',
|
||||||
|
@ -435,7 +435,7 @@ sides.
|
|||||||
),
|
),
|
||||||
Password('realm_passwd?',
|
Password('realm_passwd?',
|
||||||
cli_name='password',
|
cli_name='password',
|
||||||
label=_("Active directory domain administrator's password"),
|
label=_("Active Directory domain administrator's password"),
|
||||||
confirm=False,
|
confirm=False,
|
||||||
),
|
),
|
||||||
Str('realm_server?',
|
Str('realm_server?',
|
||||||
@ -511,6 +511,30 @@ sides.
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def interactive_prompt_callback(self, kw):
|
||||||
|
"""
|
||||||
|
Also ensure that realm_admin is prompted for if --admin or
|
||||||
|
--trust-secret is not specified when 'ipa trust-add' is run on the
|
||||||
|
system.
|
||||||
|
|
||||||
|
Also ensure that realm_passwd is prompted for if --password or
|
||||||
|
--trust-secret is not specified when 'ipa trust-add' is run on the
|
||||||
|
system.
|
||||||
|
"""
|
||||||
|
|
||||||
|
trust_secret = kw.get('trust_secret')
|
||||||
|
realm_admin = kw.get('realm_admin')
|
||||||
|
realm_passwd = kw.get('realm_passwd')
|
||||||
|
|
||||||
|
if trust_secret is None:
|
||||||
|
if realm_admin is None:
|
||||||
|
kw['realm_admin'] = self.prompt_param(
|
||||||
|
self.params['realm_admin'])
|
||||||
|
|
||||||
|
if realm_passwd is None:
|
||||||
|
kw['realm_passwd'] = self.Backend.textui.prompt_password(
|
||||||
|
self.params['realm_passwd'].label, confirm=False)
|
||||||
|
|
||||||
def validate_options(self, *keys, **options):
|
def validate_options(self, *keys, **options):
|
||||||
if not _bindings_installed:
|
if not _bindings_installed:
|
||||||
raise errors.NotFound(
|
raise errors.NotFound(
|
||||||
@ -721,11 +745,10 @@ sides.
|
|||||||
ret['summary'] = self.msg_summary_existing % ret
|
ret['summary'] = self.msg_summary_existing % ret
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# 2. We don't have access to the remote domain and trustdom password
|
# 2. We don't have access to the remote domain and trustdom password
|
||||||
# is provided. Do the work on our side and inform what to do on remote
|
# is provided. Do the work on our side and inform what to do on remote
|
||||||
# side.
|
# side.
|
||||||
if 'trust_secret' in options:
|
if options.get('trust_secret'):
|
||||||
result = self.trustinstance.join_ad_ipa_half(
|
result = self.trustinstance.join_ad_ipa_half(
|
||||||
keys[-1],
|
keys[-1],
|
||||||
self.realm_server,
|
self.realm_server,
|
||||||
@ -740,8 +763,11 @@ sides.
|
|||||||
if dn:
|
if dn:
|
||||||
ret['summary'] = self.msg_summary_existing % ret
|
ret['summary'] = self.msg_summary_existing % ret
|
||||||
return ret
|
return ret
|
||||||
raise errors.ValidationError(name=_('AD Trust setup'),
|
else:
|
||||||
error=_('Not enough arguments specified to perform trust setup'))
|
raise errors.ValidationError(
|
||||||
|
name=_('AD Trust setup'),
|
||||||
|
error=_('Not enough arguments specified to perform trust '
|
||||||
|
'setup'))
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
class trust_del(LDAPDelete):
|
class trust_del(LDAPDelete):
|
||||||
|
@ -424,6 +424,17 @@ class user(LDAPObject):
|
|||||||
],
|
],
|
||||||
'default_privileges': {'User Administrators'},
|
'default_privileges': {'User Administrators'},
|
||||||
},
|
},
|
||||||
|
'System: Read User Compat Tree': {
|
||||||
|
'non_object': True,
|
||||||
|
'ipapermbindruletype': 'anonymous',
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtarget': DN('cn=users', 'cn=compat', api.env.basedn),
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'objectclass', 'uid', 'cn', 'gecos', 'gidnumber', 'uidnumber',
|
||||||
|
'homedirectory', 'loginshell',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
label = _('Users')
|
label = _('Users')
|
||||||
@ -780,22 +791,20 @@ class user_add(LDAPCreate):
|
|||||||
if 'manager' in entry_attrs:
|
if 'manager' in entry_attrs:
|
||||||
entry_attrs['manager'] = self.obj._normalize_manager(entry_attrs['manager'])
|
entry_attrs['manager'] = self.obj._normalize_manager(entry_attrs['manager'])
|
||||||
|
|
||||||
if ('objectclass' in entry_attrs
|
if 'userclass' in entry_attrs and \
|
||||||
and 'userclass' in entry_attrs
|
'ipauser' not in entry_attrs['objectclass']:
|
||||||
and 'ipauser' not in entry_attrs['objectclass']):
|
|
||||||
entry_attrs['objectclass'].append('ipauser')
|
entry_attrs['objectclass'].append('ipauser')
|
||||||
|
|
||||||
if 'ipatokenradiusconfiglink' in entry_attrs:
|
if 'ipauserauthtype' in entry_attrs and \
|
||||||
cl = entry_attrs['ipatokenradiusconfiglink']
|
'ipauserauthtypeclass' not in entry_attrs['objectclass']:
|
||||||
if cl:
|
entry_attrs['objectclass'].append('ipauserauthtypeclass')
|
||||||
if 'objectclass' not in entry_attrs:
|
|
||||||
_entry = ldap.get_entry(dn, ['objectclass'])
|
|
||||||
entry_attrs['objectclass'] = _entry['objectclass']
|
|
||||||
|
|
||||||
|
rcl = entry_attrs.get('ipatokenradiusconfiglink', None)
|
||||||
|
if rcl:
|
||||||
if 'ipatokenradiusproxyuser' not in entry_attrs['objectclass']:
|
if 'ipatokenradiusproxyuser' not in entry_attrs['objectclass']:
|
||||||
entry_attrs['objectclass'].append('ipatokenradiusproxyuser')
|
entry_attrs['objectclass'].append('ipatokenradiusproxyuser')
|
||||||
|
|
||||||
answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
|
answer = self.api.Object['radiusproxy'].get_dn_if_exists(rcl)
|
||||||
entry_attrs['ipatokenradiusconfiglink'] = answer
|
entry_attrs['ipatokenradiusconfiglink'] = answer
|
||||||
|
|
||||||
return dn
|
return dn
|
||||||
|
@ -96,18 +96,29 @@ def find_modules_in_dir(src_dir):
|
|||||||
|
|
||||||
def validate_host_dns(log, fqdn):
|
def validate_host_dns(log, fqdn):
|
||||||
"""
|
"""
|
||||||
See if the hostname has a DNS A record.
|
See if the hostname has a DNS A/AAAA record.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
answers = resolver.query(fqdn, rdatatype.A)
|
answers = resolver.query(fqdn, rdatatype.A)
|
||||||
log.debug(
|
log.debug(
|
||||||
'IPA: found %d records for %s: %s' % (len(answers), fqdn,
|
'IPA: found %d A records for %s: %s' % (len(answers), fqdn,
|
||||||
' '.join(str(answer) for answer in answers))
|
' '.join(str(answer) for answer in answers))
|
||||||
)
|
)
|
||||||
except DNSException, e:
|
except DNSException, e:
|
||||||
log.debug(
|
log.debug(
|
||||||
'IPA: DNS A record lookup failed for %s' % fqdn
|
'IPA: DNS A record lookup failed for %s' % fqdn
|
||||||
)
|
)
|
||||||
|
# A record not found, try to find AAAA record
|
||||||
|
try:
|
||||||
|
answers = resolver.query(fqdn, rdatatype.AAAA)
|
||||||
|
log.debug(
|
||||||
|
'IPA: found %d AAAA records for %s: %s' % (len(answers), fqdn,
|
||||||
|
' '.join(str(answer) for answer in answers))
|
||||||
|
)
|
||||||
|
except DNSException, e:
|
||||||
|
log.debug(
|
||||||
|
'IPA: DNS AAAA record lookup failed for %s' % fqdn
|
||||||
|
)
|
||||||
raise errors.DNSNotARecordError()
|
raise errors.DNSNotARecordError()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Authors: Rob Crittenden <rcritten@redhat.com>
|
# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
# David Kupka <dkupka@redhat.com>
|
||||||
#
|
#
|
||||||
# Copyright (C) 2010 Red Hat
|
# Copyright (C) 2010 Red Hat
|
||||||
# see file 'COPYING' for use and warranty information
|
# see file 'COPYING' for use and warranty information
|
||||||
@ -23,97 +24,175 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
|
import dbus
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipapython import dogtag
|
from ipapython import dogtag
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
|
from ipaplatform import services
|
||||||
|
from ipapython.ipa_log_manager import root_logger
|
||||||
|
|
||||||
REQUEST_DIR = paths.CERTMONGER_REQUESTS_DIR
|
REQUEST_DIR = paths.CERTMONGER_REQUESTS_DIR
|
||||||
CA_DIR = paths.CERTMONGER_CAS_DIR
|
CA_DIR = paths.CERTMONGER_CAS_DIR
|
||||||
|
|
||||||
# Normalizer types for critera in get_request_id()
|
DBUS_CM_PATH = '/org/fedorahosted/certmonger'
|
||||||
NPATH = 1
|
DBUS_CM_IF = 'org.fedorahosted.certmonger'
|
||||||
|
DBUS_CM_REQUEST_IF = 'org.fedorahosted.certmonger.request'
|
||||||
|
DBUS_CM_CA_IF = 'org.fedorahosted.certmonger.ca'
|
||||||
|
DBUS_PROPERTY_IF = 'org.freedesktop.DBus.Properties'
|
||||||
|
|
||||||
def find_request_value(filename, directive):
|
|
||||||
|
class _cm_dbus_object(object):
|
||||||
"""
|
"""
|
||||||
Return a value from a certmonger request file for the requested directive
|
Auxiliary class for convenient DBus object handling.
|
||||||
|
|
||||||
It tries to do this a number of times because sometimes there is a delay
|
|
||||||
when ipa-getcert returns and the file is fully updated, particularly
|
|
||||||
when doing a request. Generating a CSR is fast but not instantaneous.
|
|
||||||
"""
|
"""
|
||||||
tries = 1
|
def __init__(self, bus, object_path, object_dbus_interface,
|
||||||
value = None
|
parent_dbus_interface=None, property_interface=False):
|
||||||
found = False
|
"""
|
||||||
while value is None and tries <= 5:
|
bus - DBus bus object, result of dbus.SystemBus() or dbus.SessionBus()
|
||||||
tries=tries + 1
|
Object is accesible over this DBus bus instance.
|
||||||
time.sleep(1)
|
object_path - path to requested object on DBus bus
|
||||||
fp = open(filename, 'r')
|
object_dbus_interface
|
||||||
lines = fp.readlines()
|
parent_dbus_interface
|
||||||
fp.close()
|
property_interface - create DBus property interface? True or False
|
||||||
|
"""
|
||||||
|
if bus is None or object_path is None or object_dbus_interface is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"bus, object_path and dbus_interface must not be None.")
|
||||||
|
if parent_dbus_interface is None:
|
||||||
|
parent_dbus_interface = object_dbus_interface
|
||||||
|
self.bus = bus
|
||||||
|
self.path = object_path
|
||||||
|
self.obj_dbus_if = object_dbus_interface
|
||||||
|
self.parent_dbus_if = parent_dbus_interface
|
||||||
|
self.obj = bus.get_object(parent_dbus_interface, object_path)
|
||||||
|
self.obj_if = dbus.Interface(self.obj, object_dbus_interface)
|
||||||
|
if property_interface:
|
||||||
|
self.prop_if = dbus.Interface(self.obj, DBUS_PROPERTY_IF)
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if found:
|
|
||||||
# A value can span multiple lines. If it does then it has a
|
|
||||||
# leading space.
|
|
||||||
if not line.startswith(' '):
|
|
||||||
# We hit the next directive, return now
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
value = value + line[1:]
|
|
||||||
else:
|
|
||||||
if line.startswith(directive + '='):
|
|
||||||
found = True
|
|
||||||
value = line[len(directive)+1:]
|
|
||||||
|
|
||||||
return value
|
def _start_certmonger():
|
||||||
|
"""
|
||||||
|
Start certmonger daemon. If it's already running systemctl just ignores
|
||||||
|
the command.
|
||||||
|
"""
|
||||||
|
if not services.knownservices.certmonger.is_running():
|
||||||
|
try:
|
||||||
|
services.knownservices.certmonger.start()
|
||||||
|
except Exception, e:
|
||||||
|
root_logger.error('Failed to start certmonger: %s' % e)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _connect_to_certmonger():
|
||||||
|
"""
|
||||||
|
Start certmonger daemon and connect to it via DBus.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
_start_certmonger()
|
||||||
|
except (KeyboardInterrupt, OSError), e:
|
||||||
|
root_logger.error('Failed to start certmonger: %s' % e)
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
bus = dbus.SystemBus()
|
||||||
|
cm = _cm_dbus_object(bus, DBUS_CM_PATH, DBUS_CM_IF)
|
||||||
|
except dbus.DBusException, e:
|
||||||
|
root_logger.error("Failed to access certmonger over DBus: %s", e)
|
||||||
|
raise
|
||||||
|
return cm
|
||||||
|
|
||||||
|
|
||||||
|
def _get_requests(criteria=dict()):
|
||||||
|
"""
|
||||||
|
Get all requests that matches the provided criteria.
|
||||||
|
"""
|
||||||
|
if not isinstance(criteria, dict):
|
||||||
|
raise TypeError('"criteria" must be dict.')
|
||||||
|
|
||||||
|
cm = _connect_to_certmonger()
|
||||||
|
requests = []
|
||||||
|
requests_paths = []
|
||||||
|
if 'nickname' in criteria:
|
||||||
|
request_path = cm.obj_if.find_request_by_nickname(criteria['nickname'])
|
||||||
|
if request_path:
|
||||||
|
requests_paths = [request_path]
|
||||||
|
else:
|
||||||
|
requests_paths = cm.obj_if.get_requests()
|
||||||
|
|
||||||
|
for request_path in requests_paths:
|
||||||
|
request = _cm_dbus_object(cm.bus, request_path, DBUS_CM_REQUEST_IF,
|
||||||
|
DBUS_CM_IF, True)
|
||||||
|
for criterion in criteria:
|
||||||
|
if criterion == 'ca-name':
|
||||||
|
ca_path = request.obj_if.get_ca()
|
||||||
|
ca = _cm_dbus_object(cm.bus, ca_path, DBUS_CM_CA_IF,
|
||||||
|
DBUS_CM_IF)
|
||||||
|
value = ca.obj_if.get_nickname()
|
||||||
|
else:
|
||||||
|
value = request.prop_if.Get(DBUS_CM_REQUEST_IF, criterion)
|
||||||
|
if value != criteria[criterion]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
requests.append(request)
|
||||||
|
return requests
|
||||||
|
|
||||||
|
|
||||||
|
def _get_request(criteria):
|
||||||
|
"""
|
||||||
|
Find request that matches criteria.
|
||||||
|
If 'nickname' is specified other criteria are ignored because 'nickname'
|
||||||
|
uniquely identify single request.
|
||||||
|
When multiple or none request matches specified criteria RuntimeError is
|
||||||
|
raised.
|
||||||
|
"""
|
||||||
|
requests = _get_requests(criteria)
|
||||||
|
if len(requests) == 0:
|
||||||
|
return None
|
||||||
|
elif len(requests) == 1:
|
||||||
|
return requests[0]
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Criteria expected to be met by 1 request, got %s."
|
||||||
|
% len(requests))
|
||||||
|
|
||||||
|
|
||||||
def get_request_value(request_id, directive):
|
def get_request_value(request_id, directive):
|
||||||
"""
|
"""
|
||||||
There is no guarantee that the request_id will match the filename
|
Get property of request.
|
||||||
in the certmonger requests directory, so open each one to find the
|
|
||||||
request_id.
|
|
||||||
"""
|
"""
|
||||||
fileList=os.listdir(REQUEST_DIR)
|
try:
|
||||||
for file in fileList:
|
request = _get_request(dict(nickname=request_id))
|
||||||
value = find_request_value('%s/%s' % (REQUEST_DIR, file), 'id')
|
except RuntimeError, e:
|
||||||
if value is not None and value.rstrip() == request_id:
|
root_logger.error('Failed to get request: %s' % e)
|
||||||
return find_request_value('%s/%s' % (REQUEST_DIR, file), directive)
|
raise
|
||||||
|
if request:
|
||||||
|
return request.prop_if.Get(DBUS_CM_REQUEST_IF, directive)
|
||||||
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_request_id(criteria):
|
def get_request_id(criteria):
|
||||||
"""
|
"""
|
||||||
If you don't know the certmonger request_id then try to find it by looking
|
If you don't know the certmonger request_id then try to find it by looking
|
||||||
through all the request files. An alternative would be to parse the
|
through all the requests.
|
||||||
ipa-getcert list output but this seems cleaner.
|
|
||||||
|
|
||||||
criteria is a tuple of key/value/type to search for. The more specific
|
criteria is a tuple of key/value to search for. The more specific
|
||||||
the better. An error is raised if multiple request_ids are returned for
|
the better. An error is raised if multiple request_ids are returned for
|
||||||
the same criteria.
|
the same criteria.
|
||||||
|
|
||||||
None is returned if none of the criteria match.
|
None is returned if none of the criteria match.
|
||||||
"""
|
"""
|
||||||
assert type(criteria) is tuple
|
try:
|
||||||
|
request = _get_request(criteria)
|
||||||
|
except RuntimeError, e:
|
||||||
|
root_logger.error('Failed to get request: %s' % e)
|
||||||
|
raise
|
||||||
|
|
||||||
reqid=None
|
if request:
|
||||||
fileList=os.listdir(REQUEST_DIR)
|
return request.prop_if.Get(DBUS_CM_REQUEST_IF, 'nickname')
|
||||||
for file in fileList:
|
else:
|
||||||
match = True
|
return None
|
||||||
for (key, value, valtype) in criteria:
|
|
||||||
rv = find_request_value('%s/%s' % (REQUEST_DIR, file), key)
|
|
||||||
if rv and valtype == NPATH:
|
|
||||||
rv = os.path.abspath(rv)
|
|
||||||
if rv is None or rv.rstrip() != value:
|
|
||||||
match = False
|
|
||||||
break
|
|
||||||
if match and reqid is not None:
|
|
||||||
raise RuntimeError('multiple certmonger requests match the criteria')
|
|
||||||
if match:
|
|
||||||
reqid = find_request_value('%s/%s' % (REQUEST_DIR, file), 'id').rstrip()
|
|
||||||
|
|
||||||
return reqid
|
|
||||||
|
|
||||||
def get_requests_for_dir(dir):
|
def get_requests_for_dir(dir):
|
||||||
"""
|
"""
|
||||||
@ -121,38 +200,27 @@ def get_requests_for_dir(dir):
|
|||||||
directory.
|
directory.
|
||||||
"""
|
"""
|
||||||
reqid = []
|
reqid = []
|
||||||
fileList=os.listdir(REQUEST_DIR)
|
criteria = {'cert-storage': 'NSSDB', 'key-storage': 'NSSDB',
|
||||||
for file in fileList:
|
'cert-database': dir, 'key-database': dir, }
|
||||||
rv = find_request_value(os.path.join(REQUEST_DIR, file),
|
requests = _get_requests(criteria)
|
||||||
'cert_storage_location')
|
for request in requests:
|
||||||
if rv is None:
|
reqid.append(request.prop_if.Get(DBUS_CM_REQUEST_IF, 'nickname'))
|
||||||
continue
|
|
||||||
rv = os.path.abspath(rv).rstrip()
|
|
||||||
if rv != dir:
|
|
||||||
continue
|
|
||||||
id = find_request_value(os.path.join(REQUEST_DIR, file), 'id')
|
|
||||||
if id is not None:
|
|
||||||
reqid.append(id.rstrip())
|
|
||||||
|
|
||||||
return reqid
|
return reqid
|
||||||
|
|
||||||
|
|
||||||
def add_request_value(request_id, directive, value):
|
def add_request_value(request_id, directive, value):
|
||||||
"""
|
"""
|
||||||
Add a new directive to a certmonger request file.
|
Add a new directive to a certmonger request file.
|
||||||
|
|
||||||
The certmonger service MUST be stopped in order for this to work.
|
|
||||||
"""
|
"""
|
||||||
fileList=os.listdir(REQUEST_DIR)
|
try:
|
||||||
for file in fileList:
|
request = _get_request({'nickname': request_id})
|
||||||
id = find_request_value('%s/%s' % (REQUEST_DIR, file), 'id')
|
except RuntimeError, e:
|
||||||
if id is not None and id.rstrip() == request_id:
|
root_logger.error('Failed to get request: %s' % e)
|
||||||
current_value = find_request_value('%s/%s' % (REQUEST_DIR, file), directive)
|
raise
|
||||||
if not current_value:
|
if request:
|
||||||
fp = open('%s/%s' % (REQUEST_DIR, file), 'a')
|
request.obj_if.modify({directive: value})
|
||||||
fp.write('%s=%s\n' % (directive, value))
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def add_principal(request_id, principal):
|
def add_principal(request_id, principal):
|
||||||
"""
|
"""
|
||||||
@ -161,7 +229,8 @@ def add_principal(request_id, principal):
|
|||||||
When an existing certificate is added via start-tracking it won't have
|
When an existing certificate is added via start-tracking it won't have
|
||||||
a principal.
|
a principal.
|
||||||
"""
|
"""
|
||||||
return add_request_value(request_id, 'template_principal', principal)
|
add_request_value(request_id, 'template-principal', [principal])
|
||||||
|
|
||||||
|
|
||||||
def add_subject(request_id, subject):
|
def add_subject(request_id, subject):
|
||||||
"""
|
"""
|
||||||
@ -171,47 +240,29 @@ def add_subject(request_id, subject):
|
|||||||
When an existing certificate is added via start-tracking it won't have
|
When an existing certificate is added via start-tracking it won't have
|
||||||
a subject_template set.
|
a subject_template set.
|
||||||
"""
|
"""
|
||||||
return add_request_value(request_id, 'template_subject', subject)
|
add_request_value(request_id, 'template-subject', subject)
|
||||||
|
|
||||||
|
|
||||||
def request_cert(nssdb, nickname, subject, principal, passwd_fname=None):
|
def request_cert(nssdb, nickname, subject, principal, passwd_fname=None):
|
||||||
"""
|
"""
|
||||||
Execute certmonger to request a server certificate
|
Execute certmonger to request a server certificate.
|
||||||
"""
|
"""
|
||||||
args = [paths.IPA_GETCERT,
|
cm = _connect_to_certmonger()
|
||||||
'request',
|
request_parameters = dict(KEY_STORAGE='NSSDB', CERT_STORAGE='NSSDB',
|
||||||
'-d', nssdb,
|
CERT_LOCATION=nssdb, CERT_NICKNAME=nickname,
|
||||||
'-n', nickname,
|
SUBJECT=subject, PRINCIPAL=principal,)
|
||||||
'-N', subject,
|
|
||||||
'-K', principal,
|
|
||||||
]
|
|
||||||
if passwd_fname:
|
if passwd_fname:
|
||||||
args.append('-p')
|
request_parameters['KEY_PIN_FILE'] = passwd_fname
|
||||||
args.append(os.path.abspath(passwd_fname))
|
result = cm.obj_if.add_request(request_parameters)
|
||||||
(stdout, stderr, returncode) = ipautil.run(args)
|
try:
|
||||||
# FIXME: should be some error handling around this
|
if result[0]:
|
||||||
m = re.match('New signing request "(\d+)" added', stdout)
|
request = _cm_dbus_object(cm.bus, result[1], DBUS_CM_REQUEST_IF,
|
||||||
request_id = m.group(1)
|
DBUS_CM_IF, True)
|
||||||
return request_id
|
except TypeError:
|
||||||
|
root_logger.error('Failed to get create new request.')
|
||||||
|
raise
|
||||||
|
return request.obj_if.get_nickname()
|
||||||
|
|
||||||
def cert_exists(nickname, secdir):
|
|
||||||
"""
|
|
||||||
See if a nickname exists in an NSS database.
|
|
||||||
|
|
||||||
Returns True/False
|
|
||||||
|
|
||||||
This isn't very sophisticated in that it doesn't differentiate between
|
|
||||||
a database that doesn't exist and a nickname that doesn't exist within
|
|
||||||
the database.
|
|
||||||
"""
|
|
||||||
args = [paths.CERTUTIL, "-L",
|
|
||||||
"-d", os.path.abspath(secdir),
|
|
||||||
"-n", nickname
|
|
||||||
]
|
|
||||||
(stdout, stderr, rc) = ipautil.run(args, raiseonerr=False)
|
|
||||||
if rc == 0:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def start_tracking(nickname, secdir, password_file=None, command=None):
|
def start_tracking(nickname, secdir, password_file=None, command=None):
|
||||||
"""
|
"""
|
||||||
@ -222,61 +273,54 @@ def start_tracking(nickname, secdir, password_file=None, command=None):
|
|||||||
certmonger to run when it renews a certificate. This command must
|
certmonger to run when it renews a certificate. This command must
|
||||||
reside in /usr/lib/ipa/certmonger to work with SELinux.
|
reside in /usr/lib/ipa/certmonger to work with SELinux.
|
||||||
|
|
||||||
Returns the stdout, stderr and returncode from running ipa-getcert
|
Returns True or False
|
||||||
|
|
||||||
This assumes that certmonger is already running.
|
|
||||||
"""
|
"""
|
||||||
if not cert_exists(nickname, os.path.abspath(secdir)):
|
cm = _connect_to_certmonger()
|
||||||
raise RuntimeError('Nickname "%s" doesn\'t exist in NSS database "%s"' % (nickname, secdir))
|
params = {'TRACK': True}
|
||||||
args = [paths.IPA_GETCERT, "start-tracking",
|
params['cert-nickname'] = nickname
|
||||||
"-d", os.path.abspath(secdir),
|
params['cert-database'] = os.path.abspath(secdir)
|
||||||
"-n", nickname]
|
params['cert-storage'] = 'NSSDB'
|
||||||
if password_file:
|
params['key-nickname'] = nickname
|
||||||
args.append("-p")
|
params['key-database'] = os.path.abspath(secdir)
|
||||||
args.append(os.path.abspath(password_file))
|
params['key-storage'] = 'NSSDB'
|
||||||
if command:
|
if command:
|
||||||
args.append("-C")
|
params['cert-postsave-command'] = command
|
||||||
args.append(command)
|
if password_file:
|
||||||
|
params['KEY_PIN_FILE'] = os.path.abspath(password_file)
|
||||||
|
result = cm.obj_if.add_request(params)
|
||||||
|
try:
|
||||||
|
if result[0]:
|
||||||
|
request = _cm_dbus_object(cm.bus, result[1], DBUS_CM_REQUEST_IF,
|
||||||
|
DBUS_CM_IF, True)
|
||||||
|
except TypeError, e:
|
||||||
|
root_logger.error('Failed to add new request.')
|
||||||
|
raise
|
||||||
|
return request.prop_if.Get(DBUS_CM_REQUEST_IF, 'nickname')
|
||||||
|
|
||||||
(stdout, stderr, returncode) = ipautil.run(args)
|
|
||||||
|
|
||||||
return (stdout, stderr, returncode)
|
|
||||||
|
|
||||||
def stop_tracking(secdir, request_id=None, nickname=None):
|
def stop_tracking(secdir, request_id=None, nickname=None):
|
||||||
"""
|
"""
|
||||||
Stop tracking the current request using either the request_id or nickname.
|
Stop tracking the current request using either the request_id or nickname.
|
||||||
|
|
||||||
This assumes that the certmonger service is running.
|
Returns True or False
|
||||||
"""
|
"""
|
||||||
if request_id is None and nickname is None:
|
if request_id is None and nickname is None:
|
||||||
raise RuntimeError('Both request_id and nickname are missing.')
|
raise RuntimeError('Both request_id and nickname are missing.')
|
||||||
if nickname:
|
|
||||||
# Using the nickname find the certmonger request_id
|
|
||||||
criteria = (('cert_storage_location', os.path.abspath(secdir), NPATH),('cert_nickname', nickname, None))
|
|
||||||
try:
|
|
||||||
request_id = get_request_id(criteria)
|
|
||||||
if request_id is None:
|
|
||||||
return ('', '', 0)
|
|
||||||
except RuntimeError:
|
|
||||||
# This means that multiple requests matched, skip it for now
|
|
||||||
# Fall back to trying to stop tracking using nickname
|
|
||||||
pass
|
|
||||||
|
|
||||||
args = [paths.GETCERT,
|
criteria = {'cert-database': secdir}
|
||||||
'stop-tracking',
|
|
||||||
]
|
|
||||||
if request_id:
|
if request_id:
|
||||||
args.append('-i')
|
criteria['nickname'] = request_id
|
||||||
args.append(request_id)
|
if nickname:
|
||||||
else:
|
criteria['cert-nickname'] = nickname
|
||||||
args.append('-n')
|
try:
|
||||||
args.append(nickname)
|
request = _get_request(criteria)
|
||||||
args.append('-d')
|
except RuntimeError, e:
|
||||||
args.append(os.path.abspath(secdir))
|
root_logger.error('Failed to get request: %s' % e)
|
||||||
|
raise
|
||||||
|
if request:
|
||||||
|
cm = _connect_to_certmonger()
|
||||||
|
cm.obj_if.remove_request(request.path)
|
||||||
|
|
||||||
(stdout, stderr, returncode) = ipautil.run(args)
|
|
||||||
|
|
||||||
return (stdout, stderr, returncode)
|
|
||||||
|
|
||||||
def _find_IPA_ca():
|
def _find_IPA_ca():
|
||||||
"""
|
"""
|
||||||
@ -286,13 +330,10 @@ def _find_IPA_ca():
|
|||||||
We can use find_request_value because the ca files have the
|
We can use find_request_value because the ca files have the
|
||||||
same file format.
|
same file format.
|
||||||
"""
|
"""
|
||||||
fileList=os.listdir(CA_DIR)
|
cm = _connect_to_certmonger()
|
||||||
for file in fileList:
|
ca_path = cm.obj_if.find_ca_by_nickname('IPA')
|
||||||
value = find_request_value('%s/%s' % (CA_DIR, file), 'id')
|
return _cm_dbus_object(cm.bus, ca_path, DBUS_CM_CA_IF, DBUS_CM_IF, True)
|
||||||
if value is not None and value.strip() == 'IPA':
|
|
||||||
return '%s/%s' % (CA_DIR, file)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_principal_to_cas(principal):
|
def add_principal_to_cas(principal):
|
||||||
"""
|
"""
|
||||||
@ -302,58 +343,27 @@ def add_principal_to_cas(principal):
|
|||||||
/usr/libexec/certmonger/ipa-submit.
|
/usr/libexec/certmonger/ipa-submit.
|
||||||
|
|
||||||
We also need to restore this on uninstall.
|
We also need to restore this on uninstall.
|
||||||
|
|
||||||
The certmonger service MUST be stopped in order for this to work.
|
|
||||||
"""
|
"""
|
||||||
cafile = _find_IPA_ca()
|
ca = _find_IPA_ca()
|
||||||
if cafile is None:
|
if ca:
|
||||||
return
|
ext_helper = ca.prop_if.Get(DBUS_CM_CA_IF, 'external-helper')
|
||||||
|
if ext_helper and ext_helper.find('-k') == -1:
|
||||||
|
ext_helper = '%s -k %s' % (ext_helper.strip(), principal)
|
||||||
|
ca.prop_if.Set(DBUS_CM_CA_IF, 'external-helper', ext_helper)
|
||||||
|
|
||||||
update = False
|
|
||||||
fp = open(cafile, 'r')
|
|
||||||
lines = fp.readlines()
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
for i in xrange(len(lines)):
|
|
||||||
if lines[i].startswith('ca_external_helper') and \
|
|
||||||
lines[i].find('-k') == -1:
|
|
||||||
lines[i] = '%s -k %s\n' % (lines[i].strip(), principal)
|
|
||||||
update = True
|
|
||||||
|
|
||||||
if update:
|
|
||||||
fp = open(cafile, 'w')
|
|
||||||
for line in lines:
|
|
||||||
fp.write(line)
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
def remove_principal_from_cas():
|
def remove_principal_from_cas():
|
||||||
"""
|
"""
|
||||||
Remove any -k principal options from the ipa_submit helper.
|
Remove any -k principal options from the ipa_submit helper.
|
||||||
|
|
||||||
The certmonger service MUST be stopped in order for this to work.
|
|
||||||
"""
|
"""
|
||||||
cafile = _find_IPA_ca()
|
ca = _find_IPA_ca()
|
||||||
if cafile is None:
|
if ca:
|
||||||
return
|
ext_helper = ca.prop_if.Get(DBUS_CM_CA_IF, 'external-helper')
|
||||||
|
if ext_helper and ext_helper.find('-k'):
|
||||||
|
ext_helper = ext_helper.strip()[0]
|
||||||
|
ca.prop_if.Set(DBUS_CM_CA_IF, 'external-helper', ext_helper)
|
||||||
|
|
||||||
update = False
|
|
||||||
fp = open(cafile, 'r')
|
|
||||||
lines = fp.readlines()
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
for i in xrange(len(lines)):
|
|
||||||
if lines[i].startswith('ca_external_helper') and \
|
|
||||||
lines[i].find('-k') > 0:
|
|
||||||
lines[i] = lines[i].strip().split(' ')[0] + '\n'
|
|
||||||
update = True
|
|
||||||
|
|
||||||
if update:
|
|
||||||
fp = open(cafile, 'w')
|
|
||||||
for line in lines:
|
|
||||||
fp.write(line)
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
# Routines specific to renewing dogtag CA certificates
|
|
||||||
def get_pin(token, dogtag_constants=None):
|
def get_pin(token, dogtag_constants=None):
|
||||||
"""
|
"""
|
||||||
Dogtag stores its NSS pin in a file formatted as token:PIN.
|
Dogtag stores its NSS pin in a file formatted as token:PIN.
|
||||||
@ -369,6 +379,7 @@ def get_pin(token, dogtag_constants=None):
|
|||||||
return pin.strip()
|
return pin.strip()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
|
def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
|
||||||
post_command, profile=None):
|
post_command, profile=None):
|
||||||
"""
|
"""
|
||||||
@ -383,52 +394,46 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
|
|||||||
post_command is the script to execute after a renewal is done.
|
post_command is the script to execute after a renewal is done.
|
||||||
|
|
||||||
Both commands can be None.
|
Both commands can be None.
|
||||||
|
|
||||||
Returns the stdout, stderr and returncode from running ipa-getcert
|
|
||||||
|
|
||||||
This assumes that certmonger is already running.
|
|
||||||
"""
|
"""
|
||||||
if not cert_exists(nickname, os.path.abspath(secdir)):
|
|
||||||
raise RuntimeError('Nickname "%s" doesn\'t exist in NSS database "%s"' % (nickname, secdir))
|
|
||||||
|
|
||||||
args = [paths.GETCERT, "start-tracking",
|
cm = _connect_to_certmonger()
|
||||||
"-d", os.path.abspath(secdir),
|
certmonger_cmd_template = paths.CERTMONGER_COMMAND_TEMPLATE
|
||||||
"-n", nickname,
|
|
||||||
"-c", ca,
|
|
||||||
]
|
|
||||||
|
|
||||||
if pre_command is not None:
|
params = {'TRACK': True}
|
||||||
|
params['cert-nickname'] = nickname
|
||||||
|
params['cert-database'] = os.path.abspath(secdir)
|
||||||
|
params['cert-storage'] = 'NSSDB'
|
||||||
|
params['key-nickname'] = nickname
|
||||||
|
params['key-database'] = os.path.abspath(secdir)
|
||||||
|
params['key-storage'] = 'NSSDB'
|
||||||
|
ca_path = cm.obj_if.find_ca_by_nickname(ca)
|
||||||
|
if ca_path:
|
||||||
|
params['ca'] = ca_path
|
||||||
|
if pin:
|
||||||
|
params['KEY_PIN'] = pin
|
||||||
|
if pinfile:
|
||||||
|
params['KEY_PIN_FILE'] = os.path.abspath(pinfile)
|
||||||
|
if pre_command:
|
||||||
if not os.path.isabs(pre_command):
|
if not os.path.isabs(pre_command):
|
||||||
if sys.maxsize > 2**32L:
|
if sys.maxsize > 2**32L:
|
||||||
libpath = 'lib64'
|
libpath = 'lib64'
|
||||||
else:
|
else:
|
||||||
libpath = 'lib'
|
libpath = 'lib'
|
||||||
pre_command = paths.CERTMONGER_COMMAND_TEMPLATE % (libpath, pre_command)
|
pre_command = certmonger_cmd_template % (libpath, pre_command)
|
||||||
args.append("-B")
|
params['cert-presave-command'] = pre_command
|
||||||
args.append(pre_command)
|
if post_command:
|
||||||
|
|
||||||
if post_command is not None:
|
|
||||||
if not os.path.isabs(post_command):
|
if not os.path.isabs(post_command):
|
||||||
if sys.maxsize > 2**32L:
|
if sys.maxsize > 2**32L:
|
||||||
libpath = 'lib64'
|
libpath = 'lib64'
|
||||||
else:
|
else:
|
||||||
libpath = 'lib'
|
libpath = 'lib'
|
||||||
post_command = paths.CERTMONGER_COMMAND_TEMPLATE % (libpath, post_command)
|
post_command = certmonger_cmd_template % (libpath, post_command)
|
||||||
args.append("-C")
|
params['cert-postsave-command'] = post_command
|
||||||
args.append(post_command)
|
|
||||||
|
|
||||||
if pinfile:
|
|
||||||
args.append("-p")
|
|
||||||
args.append(pinfile)
|
|
||||||
else:
|
|
||||||
args.append("-P")
|
|
||||||
args.append(pin)
|
|
||||||
|
|
||||||
if profile:
|
if profile:
|
||||||
args.append("-T")
|
params['ca-profile'] = profile
|
||||||
args.append(profile)
|
|
||||||
|
cm.obj_if.add_request(params)
|
||||||
|
|
||||||
(stdout, stderr, returncode) = ipautil.run(args, nolog=[pin])
|
|
||||||
|
|
||||||
def check_state(dirs):
|
def check_state(dirs):
|
||||||
"""
|
"""
|
||||||
@ -446,8 +451,11 @@ def check_state(dirs):
|
|||||||
|
|
||||||
return reqids
|
return reqids
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
request_id = request_cert(paths.HTTPD_ALIAS_DIR, "Test", "cn=tiger.example.com,O=IPA", "HTTP/tiger.example.com@EXAMPLE.COM")
|
request_id = request_cert(paths.HTTPD_ALIAS_DIR, "Test",
|
||||||
|
"cn=tiger.example.com,O=IPA",
|
||||||
|
"HTTP/tiger.example.com@EXAMPLE.COM")
|
||||||
csr = get_request_value(request_id, 'csr')
|
csr = get_request_value(request_id, 'csr')
|
||||||
print csr
|
print csr
|
||||||
stop_tracking(request_id)
|
stop_tracking(request_id)
|
||||||
|
@ -588,6 +588,10 @@ class DomainValidator(object):
|
|||||||
result = None
|
result = None
|
||||||
try:
|
try:
|
||||||
result = netrc.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_GC | nbt.NBT_SERVER_CLOSEST)
|
result = netrc.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_GC | nbt.NBT_SERVER_CLOSEST)
|
||||||
|
except RuntimeError, e:
|
||||||
|
try:
|
||||||
|
# If search of closest GC failed, attempt to find any one
|
||||||
|
result = netrc.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_GC)
|
||||||
except RuntimeError, e:
|
except RuntimeError, e:
|
||||||
finddc_error = e
|
finddc_error = e
|
||||||
|
|
||||||
@ -703,16 +707,19 @@ class TrustDomainInstance(object):
|
|||||||
binding_template=lambda x,y,z: u'%s:%s[%s]' % (x, y, z)
|
binding_template=lambda x,y,z: u'%s:%s[%s]' % (x, y, z)
|
||||||
return [binding_template(t, remote_host, o) for t in transports for o in options]
|
return [binding_template(t, remote_host, o) for t in transports for o in options]
|
||||||
|
|
||||||
def retrieve_anonymously(self, remote_host, discover_srv=False):
|
def retrieve_anonymously(self, remote_host, discover_srv=False, search_pdc=False):
|
||||||
"""
|
"""
|
||||||
When retrieving DC information anonymously, we can't get SID of the domain
|
When retrieving DC information anonymously, we can't get SID of the domain
|
||||||
"""
|
"""
|
||||||
netrc = net.Net(creds=self.creds, lp=self.parm)
|
netrc = net.Net(creds=self.creds, lp=self.parm)
|
||||||
|
flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE
|
||||||
|
if search_pdc:
|
||||||
|
flags = flags | nbt.NBT_SERVER_PDC
|
||||||
try:
|
try:
|
||||||
if discover_srv:
|
if discover_srv:
|
||||||
result = netrc.finddc(domain=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
|
result = netrc.finddc(domain=remote_host, flags=flags)
|
||||||
else:
|
else:
|
||||||
result = netrc.finddc(address=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
|
result = netrc.finddc(address=remote_host, flags=flags)
|
||||||
except RuntimeError, e:
|
except RuntimeError, e:
|
||||||
raise assess_dcerpc_exception(message=str(e))
|
raise assess_dcerpc_exception(message=str(e))
|
||||||
|
|
||||||
@ -723,6 +730,7 @@ class TrustDomainInstance(object):
|
|||||||
self.info['dns_forest'] = unicode(result.forest)
|
self.info['dns_forest'] = unicode(result.forest)
|
||||||
self.info['guid'] = unicode(result.domain_uuid)
|
self.info['guid'] = unicode(result.domain_uuid)
|
||||||
self.info['dc'] = unicode(result.pdc_dns_name)
|
self.info['dc'] = unicode(result.pdc_dns_name)
|
||||||
|
self.info['is_pdc'] = (result.server_type & nbt.NBT_SERVER_PDC) != 0
|
||||||
|
|
||||||
# Netlogon response doesn't contain SID of the domain.
|
# Netlogon response doesn't contain SID of the domain.
|
||||||
# We need to do rootDSE search with LDAP_SERVER_EXTENDED_DN_OID control to reveal the SID
|
# We need to do rootDSE search with LDAP_SERVER_EXTENDED_DN_OID control to reveal the SID
|
||||||
@ -771,6 +779,13 @@ class TrustDomainInstance(object):
|
|||||||
self.info['sid'] = unicode(result.sid)
|
self.info['sid'] = unicode(result.sid)
|
||||||
self.info['dc'] = remote_host
|
self.info['dc'] = remote_host
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self._pipe.QueryInfoPolicy2(self._policy_handle, lsa.LSA_POLICY_INFO_ROLE)
|
||||||
|
except RuntimeError, (num, message):
|
||||||
|
raise assess_dcerpc_exception(num=num, message=message)
|
||||||
|
|
||||||
|
self.info['is_pdc'] = (result.role == lsa.LSA_ROLE_PRIMARY)
|
||||||
|
|
||||||
def generate_auth(self, trustdom_secret):
|
def generate_auth(self, trustdom_secret):
|
||||||
def arcfour_encrypt(key, data):
|
def arcfour_encrypt(key, data):
|
||||||
c = RC4.RC4(key)
|
c = RC4.RC4(key)
|
||||||
@ -886,7 +901,7 @@ class TrustDomainInstance(object):
|
|||||||
info.sid = security.dom_sid(another_domain.info['sid'])
|
info.sid = security.dom_sid(another_domain.info['sid'])
|
||||||
info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
|
info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
|
||||||
info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
|
info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
|
||||||
info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
|
info.trust_attributes = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dname = lsa.String()
|
dname = lsa.String()
|
||||||
@ -903,8 +918,6 @@ class TrustDomainInstance(object):
|
|||||||
except RuntimeError, (num, message):
|
except RuntimeError, (num, message):
|
||||||
raise assess_dcerpc_exception(num=num, message=message)
|
raise assess_dcerpc_exception(num=num, message=message)
|
||||||
|
|
||||||
self.update_ftinfo(another_domain)
|
|
||||||
|
|
||||||
# We should use proper trustdom handle in order to modify the
|
# We should use proper trustdom handle in order to modify the
|
||||||
# trust settings. Samba insists this has to be done with LSA
|
# trust settings. Samba insists this has to be done with LSA
|
||||||
# OpenTrustedDomain* calls, it is not enough to have a handle
|
# OpenTrustedDomain* calls, it is not enough to have a handle
|
||||||
@ -923,6 +936,15 @@ class TrustDomainInstance(object):
|
|||||||
# server as that one doesn't support AES encryption types
|
# server as that one doesn't support AES encryption types
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
|
||||||
|
self._pipe.SetInformationTrustedDomain(trustdom_handle, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX, info)
|
||||||
|
except RuntimeError, e:
|
||||||
|
root_logger.error('unable to set trust to transitive: %s' % (str(e)))
|
||||||
|
pass
|
||||||
|
if self.info['is_pdc']:
|
||||||
|
self.update_ftinfo(another_domain)
|
||||||
|
|
||||||
def verify_trust(self, another_domain):
|
def verify_trust(self, another_domain):
|
||||||
def retrieve_netlogon_info_2(domain, function_code, data):
|
def retrieve_netlogon_info_2(domain, function_code, data):
|
||||||
try:
|
try:
|
||||||
@ -1017,7 +1039,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None):
|
|||||||
|
|
||||||
result = []
|
result = []
|
||||||
for t in domains.array:
|
for t in domains.array:
|
||||||
if ((t.trust_attributes & trust_attributes['NETR_TRUST_ATTRIBUTE_WITHIN_FOREST']) and
|
if (not (t.trust_flags & trust_flags['NETR_TRUST_FLAG_PRIMARY']) and
|
||||||
(t.trust_flags & trust_flags['NETR_TRUST_FLAG_IN_FOREST'])):
|
(t.trust_flags & trust_flags['NETR_TRUST_FLAG_IN_FOREST'])):
|
||||||
res = dict()
|
res = dict()
|
||||||
res['cn'] = unicode(t.dns_name)
|
res['cn'] = unicode(t.dns_name)
|
||||||
@ -1066,9 +1088,9 @@ class TrustDomainJoins(object):
|
|||||||
rd.creds.set_anonymous()
|
rd.creds.set_anonymous()
|
||||||
rd.creds.set_workstation(self.local_domain.hostname)
|
rd.creds.set_workstation(self.local_domain.hostname)
|
||||||
if realm_server is None:
|
if realm_server is None:
|
||||||
rd.retrieve_anonymously(realm, discover_srv=True)
|
rd.retrieve_anonymously(realm, discover_srv=True, search_pdc=True)
|
||||||
else:
|
else:
|
||||||
rd.retrieve_anonymously(realm_server, discover_srv=False)
|
rd.retrieve_anonymously(realm_server, discover_srv=False, search_pdc=True)
|
||||||
rd.read_only = True
|
rd.read_only = True
|
||||||
if realm_admin and realm_passwd:
|
if realm_admin and realm_passwd:
|
||||||
if 'name' in rd.info:
|
if 'name' in rd.info:
|
||||||
@ -1129,6 +1151,9 @@ class TrustDomainJoins(object):
|
|||||||
realm_passwd
|
realm_passwd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.remote_domain.info['dns_domain'] != self.remote_domain.info['dns_forest']:
|
||||||
|
raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'], domain=self.remote_domain.info['dns_domain'])
|
||||||
|
|
||||||
if not self.remote_domain.read_only:
|
if not self.remote_domain.read_only:
|
||||||
trustdom_pass = samba.generate_random_password(128, 128)
|
trustdom_pass = samba.generate_random_password(128, 128)
|
||||||
self.get_realmdomains()
|
self.get_realmdomains()
|
||||||
@ -1145,5 +1170,8 @@ class TrustDomainJoins(object):
|
|||||||
if not(isinstance(self.remote_domain, TrustDomainInstance)):
|
if not(isinstance(self.remote_domain, TrustDomainInstance)):
|
||||||
self.populate_remote_domain(realm, realm_server, realm_passwd=None)
|
self.populate_remote_domain(realm, realm_server, realm_passwd=None)
|
||||||
|
|
||||||
|
if self.remote_domain.info['dns_domain'] != self.remote_domain.info['dns_forest']:
|
||||||
|
raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'], domain=self.remote_domain.info['dns_domain'])
|
||||||
|
|
||||||
self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
|
self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
|
||||||
return dict(local=self.local_domain, remote=self.remote_domain, verified=False)
|
return dict(local=self.local_domain, remote=self.remote_domain, verified=False)
|
||||||
|
@ -408,6 +408,7 @@ class ADTRUSTInstance(service.Service):
|
|||||||
conf_fd = open(self.smb_conf, "w")
|
conf_fd = open(self.smb_conf, "w")
|
||||||
conf_fd.write('### Added by IPA Installer ###\n')
|
conf_fd.write('### Added by IPA Installer ###\n')
|
||||||
conf_fd.write('[global]\n')
|
conf_fd.write('[global]\n')
|
||||||
|
conf_fd.write('debug pid = yes\n')
|
||||||
conf_fd.write('config backend = registry\n')
|
conf_fd.write('config backend = registry\n')
|
||||||
conf_fd.close()
|
conf_fd.close()
|
||||||
|
|
||||||
@ -496,6 +497,14 @@ class ADTRUSTInstance(service.Service):
|
|||||||
def __setup_principal(self):
|
def __setup_principal(self):
|
||||||
try:
|
try:
|
||||||
api.Command.service_add(unicode(self.cifs_principal))
|
api.Command.service_add(unicode(self.cifs_principal))
|
||||||
|
except errors.DuplicateEntry:
|
||||||
|
# CIFS principal already exists, it is not the first time
|
||||||
|
# adtrustinstance is managed
|
||||||
|
# That's fine, we we'll re-extract the key again.
|
||||||
|
pass
|
||||||
|
except Exception, e:
|
||||||
|
self.print_msg("Cannot add CIFS service: %s" % e)
|
||||||
|
|
||||||
# Add the principal to the 'adtrust agents' group
|
# Add the principal to the 'adtrust agents' group
|
||||||
# as 389-ds only operates with GroupOfNames, we have to use
|
# as 389-ds only operates with GroupOfNames, we have to use
|
||||||
# the principal's proper dn as defined in self.cifs_agent
|
# the principal's proper dn as defined in self.cifs_agent
|
||||||
@ -513,11 +522,6 @@ class ADTRUSTInstance(service.Service):
|
|||||||
member=[self.cifs_agent],
|
member=[self.cifs_agent],
|
||||||
)
|
)
|
||||||
self.admin_conn.add_entry(entry)
|
self.admin_conn.add_entry(entry)
|
||||||
except Exception:
|
|
||||||
# CIFS principal already exists, it is not the first time
|
|
||||||
# adtrustinstance is managed
|
|
||||||
# That's fine, we we'll re-extract the key again.
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.clean_samba_keytab()
|
self.clean_samba_keytab()
|
||||||
|
|
||||||
|
@ -318,13 +318,13 @@ def stop_tracking_certificates(dogtag_constants):
|
|||||||
try:
|
try:
|
||||||
certmonger.stop_tracking(
|
certmonger.stop_tracking(
|
||||||
dogtag_constants.ALIAS_DIR, nickname=nickname)
|
dogtag_constants.ALIAS_DIR, nickname=nickname)
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error(
|
root_logger.error(
|
||||||
"certmonger failed to stop tracking certificate: %s" % str(e))
|
"certmonger failed to stop tracking certificate: %s" % str(e))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname='ipaCert')
|
certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname='ipaCert')
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error(
|
root_logger.error(
|
||||||
"certmonger failed to stop tracking certificate: %s" % str(e))
|
"certmonger failed to stop tracking certificate: %s" % str(e))
|
||||||
cmonger.stop()
|
cmonger.stop()
|
||||||
@ -449,6 +449,7 @@ class CAInstance(service.Service):
|
|||||||
self.step("creating pki-ca instance", self.create_instance)
|
self.step("creating pki-ca instance", self.create_instance)
|
||||||
self.step("configuring certificate server instance", self.__configure_instance)
|
self.step("configuring certificate server instance", self.__configure_instance)
|
||||||
self.step("stopping certificate server instance to update CS.cfg", self.__stop)
|
self.step("stopping certificate server instance to update CS.cfg", self.__stop)
|
||||||
|
self.step("backing up CS.cfg", self.backup_config)
|
||||||
self.step("disabling nonces", self.__disable_nonce)
|
self.step("disabling nonces", self.__disable_nonce)
|
||||||
self.step("set up CRL publishing", self.__enable_crl_publish)
|
self.step("set up CRL publishing", self.__enable_crl_publish)
|
||||||
self.step("starting certificate server instance", self.__start)
|
self.step("starting certificate server instance", self.__start)
|
||||||
@ -583,9 +584,25 @@ class CAInstance(service.Service):
|
|||||||
config.set("CA", "pki_external_csr_path", self.csr_file)
|
config.set("CA", "pki_external_csr_path", self.csr_file)
|
||||||
|
|
||||||
elif self.external == 2:
|
elif self.external == 2:
|
||||||
|
cert = x509.load_certificate_from_file(self.cert_file)
|
||||||
|
cert_file = tempfile.NamedTemporaryFile()
|
||||||
|
x509.write_certificate(cert.der_data, cert_file.name)
|
||||||
|
cert_file.flush()
|
||||||
|
|
||||||
|
cert_chain, stderr, rc = ipautil.run(
|
||||||
|
[paths.OPENSSL, 'crl2pkcs7',
|
||||||
|
'-certfile', self.cert_chain_file,
|
||||||
|
'-nocrl'])
|
||||||
|
# Dogtag chokes on the header and footer, remove them
|
||||||
|
# https://bugzilla.redhat.com/show_bug.cgi?id=1127838
|
||||||
|
cert_chain = re.search(
|
||||||
|
r'(?<=-----BEGIN PKCS7-----).*?(?=-----END PKCS7-----)',
|
||||||
|
cert_chain, re.DOTALL).group(0)
|
||||||
|
cert_chain_file = ipautil.write_tmp_file(cert_chain)
|
||||||
|
|
||||||
config.set("CA", "pki_external", "True")
|
config.set("CA", "pki_external", "True")
|
||||||
config.set("CA", "pki_external_ca_cert_path", self.cert_file)
|
config.set("CA", "pki_external_ca_cert_path", cert_file.name)
|
||||||
config.set("CA", "pki_external_ca_cert_chain_path", self.cert_chain_file)
|
config.set("CA", "pki_external_ca_cert_chain_path", cert_chain_file.name)
|
||||||
config.set("CA", "pki_external_step_two", "True")
|
config.set("CA", "pki_external_step_two", "True")
|
||||||
|
|
||||||
# Generate configuration file
|
# Generate configuration file
|
||||||
@ -602,6 +619,7 @@ class CAInstance(service.Service):
|
|||||||
'Contents of pkispawn configuration file (%s):\n%s' %
|
'Contents of pkispawn configuration file (%s):\n%s' %
|
||||||
(cfg_file, ipautil.nolog_replace(f.read(), nolog)))
|
(cfg_file, ipautil.nolog_replace(f.read(), nolog)))
|
||||||
|
|
||||||
|
self.backup_state('installed', True)
|
||||||
try:
|
try:
|
||||||
ipautil.run(args, nolog=nolog)
|
ipautil.run(args, nolog=nolog)
|
||||||
except ipautil.CalledProcessError, e:
|
except ipautil.CalledProcessError, e:
|
||||||
@ -646,6 +664,7 @@ class CAInstance(service.Service):
|
|||||||
'-redirect', 'logs=/var/log/pki-ca',
|
'-redirect', 'logs=/var/log/pki-ca',
|
||||||
'-enable_proxy'
|
'-enable_proxy'
|
||||||
]
|
]
|
||||||
|
self.backup_state('installed', True)
|
||||||
ipautil.run(args, env={'PKI_HOSTNAME':self.fqdn})
|
ipautil.run(args, env={'PKI_HOSTNAME':self.fqdn})
|
||||||
|
|
||||||
def __enable(self):
|
def __enable(self):
|
||||||
@ -717,10 +736,15 @@ class CAInstance(service.Service):
|
|||||||
args.append("-ext_csr_file")
|
args.append("-ext_csr_file")
|
||||||
args.append(self.csr_file)
|
args.append(self.csr_file)
|
||||||
elif self.external == 2:
|
elif self.external == 2:
|
||||||
|
cert = x509.load_certificate_from_file(self.cert_file)
|
||||||
|
cert_file = tempfile.NamedTemporaryFile()
|
||||||
|
x509.write_certificate(cert.der_data, cert_file.name)
|
||||||
|
cert_file.flush()
|
||||||
|
|
||||||
args.append("-external")
|
args.append("-external")
|
||||||
args.append("true")
|
args.append("true")
|
||||||
args.append("-ext_ca_cert_file")
|
args.append("-ext_ca_cert_file")
|
||||||
args.append(self.cert_file)
|
args.append(cert_file.name)
|
||||||
args.append("-ext_ca_cert_chain_file")
|
args.append("-ext_ca_cert_chain_file")
|
||||||
args.append(self.cert_chain_file)
|
args.append(self.cert_chain_file)
|
||||||
else:
|
else:
|
||||||
@ -788,6 +812,12 @@ class CAInstance(service.Service):
|
|||||||
root_logger.debug(traceback.format_exc())
|
root_logger.debug(traceback.format_exc())
|
||||||
root_logger.critical("Failed to restart the certificate server. See the installation log for details.")
|
root_logger.critical("Failed to restart the certificate server. See the installation log for details.")
|
||||||
|
|
||||||
|
def backup_config(self):
|
||||||
|
try:
|
||||||
|
backup_config(self.dogtag_constants)
|
||||||
|
except Exception, e:
|
||||||
|
root_logger.warning("Failed to backup CS.cfg: %s", e)
|
||||||
|
|
||||||
def __disable_nonce(self):
|
def __disable_nonce(self):
|
||||||
# Turn off Nonces
|
# Turn off Nonces
|
||||||
update_result = installutils.update_file(
|
update_result = installutils.update_file(
|
||||||
@ -1320,6 +1350,8 @@ class CAInstance(service.Service):
|
|||||||
if not enabled is None and not enabled:
|
if not enabled is None and not enabled:
|
||||||
self.disable()
|
self.disable()
|
||||||
|
|
||||||
|
# Just eat this state if it exists
|
||||||
|
installed = self.restore_state("installed")
|
||||||
try:
|
try:
|
||||||
if self.dogtag_constants.DOGTAG_VERSION >= 10:
|
if self.dogtag_constants.DOGTAG_VERSION >= 10:
|
||||||
ipautil.run([paths.PKIDESTROY, "-i",
|
ipautil.run([paths.PKIDESTROY, "-i",
|
||||||
@ -1355,9 +1387,12 @@ class CAInstance(service.Service):
|
|||||||
|
|
||||||
# remove CRL files
|
# remove CRL files
|
||||||
root_logger.info("Remove old CRL files")
|
root_logger.info("Remove old CRL files")
|
||||||
|
try:
|
||||||
for f in get_crl_files():
|
for f in get_crl_files():
|
||||||
root_logger.debug("Remove %s", f)
|
root_logger.debug("Remove %s", f)
|
||||||
installutils.remove_file(f)
|
installutils.remove_file(f)
|
||||||
|
except OSError, e:
|
||||||
|
root_logger.warning("Error while removing old CRL files: %s" % e)
|
||||||
|
|
||||||
# remove CRL directory
|
# remove CRL directory
|
||||||
root_logger.info("Remove CRL directory")
|
root_logger.info("Remove CRL directory")
|
||||||
@ -1417,7 +1452,7 @@ class CAInstance(service.Service):
|
|||||||
secdir=paths.HTTPD_ALIAS_DIR,
|
secdir=paths.HTTPD_ALIAS_DIR,
|
||||||
pre_command=None,
|
pre_command=None,
|
||||||
post_command='renew_ra_cert')
|
post_command='renew_ra_cert')
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error(
|
root_logger.error(
|
||||||
"certmonger failed to start tracking certificate: %s" % e)
|
"certmonger failed to start tracking certificate: %s" % e)
|
||||||
|
|
||||||
@ -1445,7 +1480,7 @@ class CAInstance(service.Service):
|
|||||||
secdir=self.dogtag_constants.ALIAS_DIR,
|
secdir=self.dogtag_constants.ALIAS_DIR,
|
||||||
pre_command='stop_pkicad',
|
pre_command='stop_pkicad',
|
||||||
post_command='renew_ca_cert "%s"' % nickname)
|
post_command='renew_ca_cert "%s"' % nickname)
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error(
|
root_logger.error(
|
||||||
"certmonger failed to start tracking certificate: %s" % e)
|
"certmonger failed to start tracking certificate: %s" % e)
|
||||||
|
|
||||||
@ -1465,7 +1500,7 @@ class CAInstance(service.Service):
|
|||||||
secdir=self.dogtag_constants.ALIAS_DIR,
|
secdir=self.dogtag_constants.ALIAS_DIR,
|
||||||
pre_command=None,
|
pre_command=None,
|
||||||
post_command=None)
|
post_command=None)
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error(
|
root_logger.error(
|
||||||
"certmonger failed to start tracking certificate: %s" % e)
|
"certmonger failed to start tracking certificate: %s" % e)
|
||||||
|
|
||||||
@ -1596,12 +1631,15 @@ class CAInstance(service.Service):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_renewal_master(self):
|
def is_renewal_master(self, fqdn=None):
|
||||||
|
if fqdn is None:
|
||||||
|
fqdn = api.env.host
|
||||||
|
|
||||||
if not self.admin_conn:
|
if not self.admin_conn:
|
||||||
self.ldap_connect()
|
self.ldap_connect()
|
||||||
|
|
||||||
dn = DN(('cn', 'CA'), ('cn', api.env.host), ('cn', 'masters'),
|
dn = DN(('cn', 'CA'), ('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'),
|
||||||
('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
|
('cn', 'etc'), api.env.basedn)
|
||||||
filter = '(ipaConfigString=caRenewalMaster)'
|
filter = '(ipaConfigString=caRenewalMaster)'
|
||||||
try:
|
try:
|
||||||
self.admin_conn.get_entries(base_dn=dn, filter=filter,
|
self.admin_conn.get_entries(base_dn=dn, filter=filter,
|
||||||
@ -1611,6 +1649,38 @@ class CAInstance(service.Service):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def set_renewal_master(self, fqdn=None):
|
||||||
|
if fqdn is None:
|
||||||
|
fqdn = api.env.host
|
||||||
|
|
||||||
|
if not self.admin_conn:
|
||||||
|
self.ldap_connect()
|
||||||
|
|
||||||
|
base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
|
||||||
|
api.env.basedn)
|
||||||
|
filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
|
||||||
|
try:
|
||||||
|
entries = self.admin_conn.get_entries(
|
||||||
|
base_dn=base_dn, filter=filter, attrs_list=['ipaConfigString'])
|
||||||
|
except errors.NotFound:
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
dn = DN(('cn', 'CA'), ('cn', fqdn), base_dn)
|
||||||
|
master_entry = self.admin_conn.get_entry(dn, ['ipaConfigString'])
|
||||||
|
|
||||||
|
for entry in entries:
|
||||||
|
if master_entry is not None and entry.dn == master_entry.dn:
|
||||||
|
master_entry = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry['ipaConfigString'] = [x for x in entry['ipaConfigString']
|
||||||
|
if x.lower() != 'carenewalmaster']
|
||||||
|
self.admin_conn.update_entry(entry)
|
||||||
|
|
||||||
|
if master_entry is not None:
|
||||||
|
master_entry['ipaConfigString'].append('caRenewalMaster')
|
||||||
|
self.admin_conn.update_entry(master_entry)
|
||||||
|
|
||||||
|
|
||||||
def replica_ca_install_check(config):
|
def replica_ca_install_check(config):
|
||||||
if not config.setup_ca:
|
if not config.setup_ca:
|
||||||
@ -1740,6 +1810,16 @@ def install_replica_ca(config, postinstall=False):
|
|||||||
|
|
||||||
return ca
|
return ca
|
||||||
|
|
||||||
|
def backup_config(dogtag_constants=None):
|
||||||
|
"""
|
||||||
|
Create a backup copy of CS.cfg
|
||||||
|
"""
|
||||||
|
if dogtag_constants is None:
|
||||||
|
dogtag_constants = dogtag.configured_constants()
|
||||||
|
|
||||||
|
shutil.copy(dogtag_constants.CS_CFG_PATH,
|
||||||
|
dogtag_constants.CS_CFG_PATH + '.ipabkp')
|
||||||
|
|
||||||
def update_cert_config(nickname, cert, dogtag_constants=None):
|
def update_cert_config(nickname, cert, dogtag_constants=None):
|
||||||
"""
|
"""
|
||||||
When renewing a CA subsystem certificate the configuration file
|
When renewing a CA subsystem certificate the configuration file
|
||||||
@ -1761,6 +1841,10 @@ def update_cert_config(nickname, cert, dogtag_constants=None):
|
|||||||
|
|
||||||
with stopped_service(dogtag_constants.SERVICE_NAME,
|
with stopped_service(dogtag_constants.SERVICE_NAME,
|
||||||
instance_name=dogtag_constants.PKI_INSTANCE_NAME):
|
instance_name=dogtag_constants.PKI_INSTANCE_NAME):
|
||||||
|
try:
|
||||||
|
backup_config(dogtag_constants)
|
||||||
|
except Exception, e:
|
||||||
|
syslog.syslog(syslog.LOG_ERR, "Failed to backup CS.cfg: %s" % e)
|
||||||
|
|
||||||
installutils.set_directive(dogtag.configured_constants().CS_CFG_PATH,
|
installutils.set_directive(dogtag.configured_constants().CS_CFG_PATH,
|
||||||
directives[nickname],
|
directives[nickname],
|
||||||
|
@ -209,9 +209,21 @@ class NSSDatabase(object):
|
|||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Setting trust on %s failed" % root_nickname)
|
"Setting trust on %s failed" % root_nickname)
|
||||||
|
|
||||||
|
def get_cert(self, nickname, pem=False):
|
||||||
|
args = ['-L', '-n', nickname]
|
||||||
|
if pem:
|
||||||
|
args.append('-a')
|
||||||
|
else:
|
||||||
|
args.append('-r')
|
||||||
|
try:
|
||||||
|
cert, err, returncode = self.run_certutil(args)
|
||||||
|
except ipautil.CalledProcessError:
|
||||||
|
raise RuntimeError("Failed to get %s" % nickname)
|
||||||
|
return cert
|
||||||
|
|
||||||
def export_pem_cert(self, nickname, location):
|
def export_pem_cert(self, nickname, location):
|
||||||
"""Export the given cert to PEM file in the given location"""
|
"""Export the given cert to PEM file in the given location"""
|
||||||
cert, err, returncode = self.run_certutil(["-L", "-n", nickname, "-a"])
|
cert = self.get_cert(nickname)
|
||||||
with open(location, "w+") as fd:
|
with open(location, "w+") as fd:
|
||||||
fd.write(cert)
|
fd.write(cert)
|
||||||
os.chmod(location, 0444)
|
os.chmod(location, 0444)
|
||||||
@ -510,46 +522,26 @@ class CertDB(object):
|
|||||||
else:
|
else:
|
||||||
libpath = 'lib'
|
libpath = 'lib'
|
||||||
command = paths.CERTMONGER_COMMAND_TEMPLATE % (libpath, command)
|
command = paths.CERTMONGER_COMMAND_TEMPLATE % (libpath, command)
|
||||||
cmonger = services.knownservices.certmonger
|
|
||||||
cmonger.enable()
|
|
||||||
services.knownservices.messagebus.start()
|
|
||||||
cmonger.start()
|
|
||||||
try:
|
try:
|
||||||
(stdout, stderr, rc) = certmonger.start_tracking(nickname, self.secdir, password_file, command)
|
request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command)
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error("certmonger failed starting to track certificate: %s" % str(e))
|
root_logger.error("certmonger failed starting to track certificate: %s" % str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
cmonger.stop()
|
|
||||||
cert = self.get_cert_from_db(nickname)
|
cert = self.get_cert_from_db(nickname)
|
||||||
nsscert = x509.load_certificate(cert, dbdir=self.secdir)
|
nsscert = x509.load_certificate(cert, dbdir=self.secdir)
|
||||||
subject = str(nsscert.subject)
|
subject = str(nsscert.subject)
|
||||||
m = re.match('New tracking request "(\d+)" added', stdout)
|
|
||||||
if not m:
|
|
||||||
root_logger.error('Didn\'t get new %s request, got %s' % (cmonger.service_name, stdout))
|
|
||||||
raise RuntimeError('%s did not issue new tracking request for \'%s\' in \'%s\'. Use \'ipa-getcert list\' to list existing certificates.' % (cmonger.service_name, nickname, self.secdir))
|
|
||||||
request_id = m.group(1)
|
|
||||||
|
|
||||||
certmonger.add_principal(request_id, principal)
|
certmonger.add_principal(request_id, principal)
|
||||||
certmonger.add_subject(request_id, subject)
|
certmonger.add_subject(request_id, subject)
|
||||||
|
|
||||||
cmonger.start()
|
|
||||||
|
|
||||||
def untrack_server_cert(self, nickname):
|
def untrack_server_cert(self, nickname):
|
||||||
"""
|
"""
|
||||||
Tell certmonger to stop tracking the given certificate nickname.
|
Tell certmonger to stop tracking the given certificate nickname.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Always start certmonger. We can't untrack something if it isn't
|
|
||||||
# running
|
|
||||||
cmonger = services.knownservices.certmonger
|
|
||||||
services.knownservices.messagebus.start()
|
|
||||||
cmonger.start()
|
|
||||||
try:
|
try:
|
||||||
certmonger.stop_tracking(self.secdir, nickname=nickname)
|
certmonger.stop_tracking(self.secdir, nickname=nickname)
|
||||||
except (ipautil.CalledProcessError, RuntimeError), e:
|
except RuntimeError, e:
|
||||||
root_logger.error("certmonger failed to stop tracking certificate: %s" % str(e))
|
root_logger.error("certmonger failed to stop tracking certificate: %s" % str(e))
|
||||||
cmonger.stop()
|
|
||||||
|
|
||||||
def create_server_cert(self, nickname, hostname, other_certdb=None, subject=None):
|
def create_server_cert(self, nickname, hostname, other_certdb=None, subject=None):
|
||||||
"""
|
"""
|
||||||
|
@ -221,14 +221,14 @@ info: IPA V2.0
|
|||||||
|
|
||||||
class DsInstance(service.Service):
|
class DsInstance(service.Service):
|
||||||
def __init__(self, realm_name=None, domain_name=None, dm_password=None,
|
def __init__(self, realm_name=None, domain_name=None, dm_password=None,
|
||||||
fstore=None, cert_nickname='Server-Cert'):
|
fstore=None):
|
||||||
service.Service.__init__(self, "dirsrv",
|
service.Service.__init__(self, "dirsrv",
|
||||||
service_desc="directory server",
|
service_desc="directory server",
|
||||||
dm_password=dm_password,
|
dm_password=dm_password,
|
||||||
ldapi=False,
|
ldapi=False,
|
||||||
autobind=service.DISABLED
|
autobind=service.DISABLED
|
||||||
)
|
)
|
||||||
self.nickname = cert_nickname
|
self.nickname = 'Server-Cert'
|
||||||
self.dm_password = dm_password
|
self.dm_password = dm_password
|
||||||
self.realm = realm_name
|
self.realm = realm_name
|
||||||
self.sub_dict = None
|
self.sub_dict = None
|
||||||
@ -632,24 +632,23 @@ class DsInstance(service.Service):
|
|||||||
raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])
|
raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])
|
||||||
|
|
||||||
# We only handle one server cert
|
# We only handle one server cert
|
||||||
nickname = server_certs[0][0]
|
self.nickname = server_certs[0][0]
|
||||||
self.dercert = dsdb.get_cert_from_db(nickname, pem=False)
|
self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)
|
||||||
else:
|
else:
|
||||||
nickname = self.nickname
|
|
||||||
cadb = certs.CertDB(self.realm, host_name=self.fqdn, subject_base=self.subject_base)
|
cadb = certs.CertDB(self.realm, host_name=self.fqdn, subject_base=self.subject_base)
|
||||||
|
|
||||||
# FIXME, need to set this nickname in the RA plugin
|
# FIXME, need to set this nickname in the RA plugin
|
||||||
cadb.export_ca_cert('ipaCert', False)
|
cadb.export_ca_cert('ipaCert', False)
|
||||||
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
||||||
self.dercert = dsdb.create_server_cert(
|
self.dercert = dsdb.create_server_cert(
|
||||||
nickname, self.fqdn, cadb)
|
self.nickname, self.fqdn, cadb)
|
||||||
dsdb.create_pin_file()
|
dsdb.create_pin_file()
|
||||||
|
|
||||||
self.cacert_name = dsdb.cacert_name
|
self.cacert_name = dsdb.cacert_name
|
||||||
|
|
||||||
if self.ca_is_configured:
|
if self.ca_is_configured:
|
||||||
dsdb.track_server_cert(
|
dsdb.track_server_cert(
|
||||||
nickname, self.principal, dsdb.passwd_fname,
|
self.nickname, self.principal, dsdb.passwd_fname,
|
||||||
'restart_dirsrv %s' % self.serverid)
|
'restart_dirsrv %s' % self.serverid)
|
||||||
|
|
||||||
conn = ipaldap.IPAdmin(self.fqdn)
|
conn = ipaldap.IPAdmin(self.fqdn)
|
||||||
@ -670,7 +669,7 @@ class DsInstance(service.Service):
|
|||||||
DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
|
DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
|
||||||
objectclass=["top", "nsEncryptionModule"],
|
objectclass=["top", "nsEncryptionModule"],
|
||||||
cn=["RSA"],
|
cn=["RSA"],
|
||||||
nsSSLPersonalitySSL=[nickname],
|
nsSSLPersonalitySSL=[self.nickname],
|
||||||
nsSSLToken=["internal (software)"],
|
nsSSLToken=["internal (software)"],
|
||||||
nsSSLActivation=["on"],
|
nsSSLActivation=["on"],
|
||||||
)
|
)
|
||||||
|
@ -487,7 +487,7 @@ def get_server_ip_address(host_name, fstore, unattended, options):
|
|||||||
hosts_record = record_in_hosts(ip_address)
|
hosts_record = record_in_hosts(ip_address)
|
||||||
|
|
||||||
if hosts_record is None:
|
if hosts_record is None:
|
||||||
if ip_add_to_hosts:
|
if ip_add_to_hosts or options.setup_dns:
|
||||||
print "Adding ["+ip_address+" "+host_name+"] to your /etc/hosts file"
|
print "Adding ["+ip_address+" "+host_name+"] to your /etc/hosts file"
|
||||||
fstore.backup_file(paths.HOSTS)
|
fstore.backup_file(paths.HOSTS)
|
||||||
add_record_to_hosts(ip_address, host_name)
|
add_record_to_hosts(ip_address, host_name)
|
||||||
@ -734,8 +734,6 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
|
|||||||
|
|
||||||
This is used for files given to --*_pkcs12 to ipa-server-install and
|
This is used for files given to --*_pkcs12 to ipa-server-install and
|
||||||
ipa-replica-prepare.
|
ipa-replica-prepare.
|
||||||
|
|
||||||
Return a (server cert name, CA cert names) tuple
|
|
||||||
"""
|
"""
|
||||||
pkcs12_filename, pkcs12_passwd = pkcs12_info
|
pkcs12_filename, pkcs12_passwd = pkcs12_info
|
||||||
root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
|
root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
|
||||||
@ -746,13 +744,18 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
|
|||||||
# Import the CA cert first so it has a known nickname
|
# Import the CA cert first so it has a known nickname
|
||||||
# (if it's present in the PKCS#12 it won't be overwritten)
|
# (if it's present in the PKCS#12 it won't be overwritten)
|
||||||
ca_cert_name = 'The Root CA'
|
ca_cert_name = 'The Root CA'
|
||||||
|
if ca_file:
|
||||||
try:
|
try:
|
||||||
nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
|
nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
|
||||||
except (ValueError, RuntimeError) as e:
|
except (ValueError, RuntimeError) as e:
|
||||||
raise ScriptError(str(e))
|
raise ScriptError(str(e))
|
||||||
|
|
||||||
# Import everything in the PKCS#12
|
# Import everything in the PKCS#12
|
||||||
nssdb.import_pkcs12(pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
|
try:
|
||||||
|
nssdb.import_pkcs12(
|
||||||
|
pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
|
||||||
|
except RuntimeError as e:
|
||||||
|
raise ScriptError(str(e))
|
||||||
|
|
||||||
# Check we have exactly one server cert (one with a private key)
|
# Check we have exactly one server cert (one with a private key)
|
||||||
server_certs = nssdb.find_server_certs()
|
server_certs = nssdb.find_server_certs()
|
||||||
@ -767,21 +770,23 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
|
|||||||
|
|
||||||
# Check we have the whole cert chain & the CA is in it
|
# Check we have the whole cert chain & the CA is in it
|
||||||
trust_chain = nssdb.get_trust_chain(server_cert_name)
|
trust_chain = nssdb.get_trust_chain(server_cert_name)
|
||||||
while trust_chain:
|
if len(trust_chain) < 2:
|
||||||
if trust_chain[0] == ca_cert_name:
|
if ca_file:
|
||||||
break
|
raise ScriptError(
|
||||||
trust_chain = trust_chain[1:]
|
'%s is not signed by %s, or the full certificate chain is '
|
||||||
|
'not present in the PKCS#12 file' %
|
||||||
|
(pkcs12_filename, ca_file))
|
||||||
else:
|
else:
|
||||||
raise ScriptError(
|
raise ScriptError(
|
||||||
'%s is not signed by %s, or the full certificate chain is not '
|
'The full certificate chain is not present in %s' %
|
||||||
'present in the PKCS#12 file' % (pkcs12_filename, ca_file))
|
pkcs12_filename)
|
||||||
if len(trust_chain) != 2:
|
if ca_file and trust_chain[-2] != ca_cert_name:
|
||||||
raise ScriptError(
|
raise ScriptError(
|
||||||
'trust chain of the server certificate in %s contains %s '
|
'%s is not signed by %s' % (pkcs12_filename, ca_file))
|
||||||
'certificates, expected 2' %
|
ca_cert_name = trust_chain[-2]
|
||||||
(pkcs12_filename, len(trust_chain)))
|
|
||||||
|
|
||||||
# Check server validity
|
# Check server validity
|
||||||
|
nssdb.trust_root_cert(ca_cert_name)
|
||||||
try:
|
try:
|
||||||
nssdb.verify_server_cert_validity(server_cert_name, hostname)
|
nssdb.verify_server_cert_validity(server_cert_name, hostname)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@ -789,8 +794,7 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
|
|||||||
'The server certificate in %s is not valid: %s' %
|
'The server certificate in %s is not valid: %s' %
|
||||||
(pkcs12_filename, e))
|
(pkcs12_filename, e))
|
||||||
|
|
||||||
return server_cert_name
|
return nssdb.get_cert(ca_cert_name)
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def private_ccache(path=None):
|
def private_ccache(path=None):
|
||||||
|
@ -139,7 +139,7 @@ class ReplicaPrepare(admintool.AdminTool):
|
|||||||
"could not find directory instance: %s" % config_dir)
|
"could not find directory instance: %s" % config_dir)
|
||||||
|
|
||||||
def check_pkcs12(self, pkcs12_file, pkcs12_pin):
|
def check_pkcs12(self, pkcs12_file, pkcs12_pin):
|
||||||
installutils.check_pkcs12(
|
return installutils.check_pkcs12(
|
||||||
pkcs12_info=(pkcs12_file, pkcs12_pin),
|
pkcs12_info=(pkcs12_file, pkcs12_pin),
|
||||||
ca_file=CACERT,
|
ca_file=CACERT,
|
||||||
hostname=self.replica_fqdn)
|
hostname=self.replica_fqdn)
|
||||||
@ -221,7 +221,8 @@ class ReplicaPrepare(admintool.AdminTool):
|
|||||||
if options.http_pin is None:
|
if options.http_pin is None:
|
||||||
raise admintool.ScriptError(
|
raise admintool.ScriptError(
|
||||||
"%s unlock password required" % options.http_pkcs12)
|
"%s unlock password required" % options.http_pkcs12)
|
||||||
self.check_pkcs12(options.http_pkcs12, options.http_pin)
|
http_ca_cert = self.check_pkcs12(
|
||||||
|
options.http_pkcs12, options.http_pin)
|
||||||
|
|
||||||
if options.dirsrv_pkcs12:
|
if options.dirsrv_pkcs12:
|
||||||
if options.dirsrv_pin is None:
|
if options.dirsrv_pin is None:
|
||||||
@ -231,7 +232,8 @@ class ReplicaPrepare(admintool.AdminTool):
|
|||||||
if options.dirsrv_pin is None:
|
if options.dirsrv_pin is None:
|
||||||
raise admintool.ScriptError(
|
raise admintool.ScriptError(
|
||||||
"%s unlock password required" % options.dirsrv_pkcs12)
|
"%s unlock password required" % options.dirsrv_pkcs12)
|
||||||
self.check_pkcs12(options.dirsrv_pkcs12, options.dirsrv_pin)
|
dirsrv_ca_cert = self.check_pkcs12(
|
||||||
|
options.dirsrv_pkcs12, options.dirsrv_pin)
|
||||||
|
|
||||||
if options.pkinit_pkcs12:
|
if options.pkinit_pkcs12:
|
||||||
if options.pkinit_pin is None:
|
if options.pkinit_pin is None:
|
||||||
@ -242,6 +244,12 @@ class ReplicaPrepare(admintool.AdminTool):
|
|||||||
raise admintool.ScriptError(
|
raise admintool.ScriptError(
|
||||||
"%s unlock password required" % options.pkinit_pkcs12)
|
"%s unlock password required" % options.pkinit_pkcs12)
|
||||||
|
|
||||||
|
if (options.http_pkcs12 and options.dirsrv_pkcs12 and
|
||||||
|
http_ca_cert != dirsrv_ca_cert):
|
||||||
|
raise admintool.ScriptError(
|
||||||
|
"%s and %s are not signed by the same CA certificate" %
|
||||||
|
(options.http_pkcs12, options.dirsrv_pkcs12))
|
||||||
|
|
||||||
if (not ipautil.file_exists(
|
if (not ipautil.file_exists(
|
||||||
dogtag.configured_constants().CS_CFG_PATH) and
|
dogtag.configured_constants().CS_CFG_PATH) and
|
||||||
options.dirsrv_pin is None):
|
options.dirsrv_pin is None):
|
||||||
|
@ -154,7 +154,7 @@ class ServerCertInstall(admintool.AdminTool):
|
|||||||
os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
|
os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
|
||||||
|
|
||||||
def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
|
def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
|
||||||
server_cert = installutils.check_pkcs12(
|
installutils.check_pkcs12(
|
||||||
pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
|
pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
|
||||||
ca_file=CACERT,
|
ca_file=CACERT,
|
||||||
hostname=api.env.host)
|
hostname=api.env.host)
|
||||||
@ -166,6 +166,7 @@ class ServerCertInstall(admintool.AdminTool):
|
|||||||
|
|
||||||
cdb.delete_cert(old_cert)
|
cdb.delete_cert(old_cert)
|
||||||
cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
|
cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
|
||||||
|
server_cert = cdb.find_server_certs()[0][0]
|
||||||
|
|
||||||
if api.env.enable_ra:
|
if api.env.enable_ra:
|
||||||
cdb.track_server_cert(server_cert, principal, cdb.passwd_fname,
|
cdb.track_server_cert(server_cert, principal, cdb.passwd_fname,
|
||||||
|
@ -52,10 +52,10 @@ class update_ca_renewal_master(PostUpdate):
|
|||||||
self.debug("found CA renewal master %s", entries[0].dn[1].value)
|
self.debug("found CA renewal master %s", entries[0].dn[1].value)
|
||||||
return (False, False, [])
|
return (False, False, [])
|
||||||
|
|
||||||
criteria = (
|
criteria = {
|
||||||
('cert_storage_location', paths.HTTPD_ALIAS_DIR, certmonger.NPATH),
|
'cert-database': paths.HTTPD_ALIAS_DIR,
|
||||||
('cert_nickname', 'ipaCert', None),
|
'cert-nickname': 'ipaCert',
|
||||||
)
|
}
|
||||||
request_id = certmonger.get_request_id(criteria)
|
request_id = certmonger.get_request_id(criteria)
|
||||||
if request_id is not None:
|
if request_id is not None:
|
||||||
self.debug("found certmonger request for ipaCert")
|
self.debug("found certmonger request for ipaCert")
|
||||||
|
@ -61,6 +61,8 @@ class update_dnszones(PostUpdate):
|
|||||||
|
|
||||||
def execute(self, **options):
|
def execute(self, **options):
|
||||||
ldap = self.obj.backend
|
ldap = self.obj.backend
|
||||||
|
if not dns_container_exists(ldap):
|
||||||
|
return (False, False, [])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zones = api.Command.dnszone_find(all=True)['result']
|
zones = api.Command.dnszone_find(all=True)['result']
|
||||||
@ -153,6 +155,8 @@ class update_check_forwardzones(PreSchemaUpdate):
|
|||||||
# no upgrade is needed
|
# no upgrade is needed
|
||||||
return (False, False, [])
|
return (False, False, [])
|
||||||
ldap = self.obj.backend
|
ldap = self.obj.backend
|
||||||
|
if not dns_container_exists(ldap): # No DNS installed
|
||||||
|
return (False, False, [])
|
||||||
result = ldap.schema.get_obj(_ldap.schema.models.ObjectClass, 'idnsforwardzone')
|
result = ldap.schema.get_obj(_ldap.schema.models.ObjectClass, 'idnsforwardzone')
|
||||||
if result is None:
|
if result is None:
|
||||||
sysupgrade.set_upgrade_state('dns', 'update_to_forward_zones', True)
|
sysupgrade.set_upgrade_state('dns', 'update_to_forward_zones', True)
|
||||||
|
@ -96,6 +96,24 @@ from ipaserver.install.plugins.baseupdate import PostUpdate
|
|||||||
register = Registry()
|
register = Registry()
|
||||||
|
|
||||||
NONOBJECT_PERMISSIONS = {
|
NONOBJECT_PERMISSIONS = {
|
||||||
|
'System: Read Timestamp and USN Operational Attributes': {
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtargetfilter': {'(objectclass=*)'},
|
||||||
|
'ipapermbindruletype': 'anonymous',
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'createtimestamp', 'modifytimestamp', 'entryusn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'System: Read Creator and Modifier Operational Attributes': {
|
||||||
|
'ipapermlocation': api.env.basedn,
|
||||||
|
'ipapermtargetfilter': {'(objectclass=*)'},
|
||||||
|
'ipapermbindruletype': 'all',
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'creatorsname', 'modifiersname',
|
||||||
|
},
|
||||||
|
},
|
||||||
'System: Read IPA Masters': {
|
'System: Read IPA Masters': {
|
||||||
'replaces_global_anonymous_aci': True,
|
'replaces_global_anonymous_aci': True,
|
||||||
'ipapermlocation': DN('cn=masters,cn=ipa,cn=etc', api.env.basedn),
|
'ipapermlocation': DN('cn=masters,cn=ipa,cn=etc', api.env.basedn),
|
||||||
|
@ -178,15 +178,24 @@ class ldap2(LDAPClient, CrudBackend):
|
|||||||
# ignore when trying to unbind multiple times
|
# ignore when trying to unbind multiple times
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def find_entries(self, filter=None, attrs_list=None, base_dn=None,
|
def find_entries(self, filter=None, attrs_list=None, base_dn=None,
|
||||||
scope=_ldap.SCOPE_SUBTREE, time_limit=None,
|
scope=_ldap.SCOPE_SUBTREE, time_limit=None,
|
||||||
size_limit=None, search_refs=False, paged_search=False):
|
size_limit=None, search_refs=False, paged_search=False):
|
||||||
if time_limit is None or size_limit is None:
|
|
||||||
|
def _get_limits():
|
||||||
|
"""Get configured global limits, caching them for more calls"""
|
||||||
|
if not _lims:
|
||||||
config = self.get_ipa_config()
|
config = self.get_ipa_config()
|
||||||
|
_lims['time'] = config.get('ipasearchtimelimit', [None])[0]
|
||||||
|
_lims['size'] = config.get('ipasearchrecordslimit', [None])[0]
|
||||||
|
return _lims
|
||||||
|
_lims = {}
|
||||||
|
|
||||||
if time_limit is None:
|
if time_limit is None:
|
||||||
time_limit = config.get('ipasearchtimelimit', [None])[0]
|
time_limit = _get_limits()['time']
|
||||||
if size_limit is None:
|
if size_limit is None:
|
||||||
size_limit = config.get('ipasearchrecordslimit', [None])[0]
|
size_limit = _get_limits()['size']
|
||||||
|
|
||||||
has_memberindirect = False
|
has_memberindirect = False
|
||||||
has_memberofindirect = False
|
has_memberofindirect = False
|
||||||
@ -207,6 +216,20 @@ class ldap2(LDAPClient, CrudBackend):
|
|||||||
search_refs=search_refs, paged_search=paged_search)
|
search_refs=search_refs, paged_search=paged_search)
|
||||||
|
|
||||||
if has_memberindirect or has_memberofindirect:
|
if has_memberindirect or has_memberofindirect:
|
||||||
|
|
||||||
|
# For the memberof searches, we want to apply the global limit
|
||||||
|
# if it's larger than the requested one, so decreasing limits on
|
||||||
|
# the individual query only affects the query itself.
|
||||||
|
# See https://fedorahosted.org/freeipa/ticket/4398
|
||||||
|
def _max_with_none(a, b):
|
||||||
|
"""Maximum of a and b, treating None as infinity"""
|
||||||
|
if a is None or b is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return max(a, b)
|
||||||
|
time_limit = _max_with_none(time_limit, _get_limits()['time'])
|
||||||
|
size_limit = _max_with_none(size_limit, _get_limits()['size'])
|
||||||
|
|
||||||
for entry in res:
|
for entry in res:
|
||||||
if has_memberindirect:
|
if has_memberindirect:
|
||||||
self._process_memberindirect(
|
self._process_memberindirect(
|
||||||
|
@ -39,7 +39,7 @@ from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
|
|||||||
from ipalib.backend import Executioner
|
from ipalib.backend import Executioner
|
||||||
from ipalib.errors import (PublicError, InternalError, CommandError, JSONError,
|
from ipalib.errors import (PublicError, InternalError, CommandError, JSONError,
|
||||||
CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
|
CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
|
||||||
ExecutionError)
|
ExecutionError, PasswordExpired)
|
||||||
from ipalib.request import context, destroy_context
|
from ipalib.request import context, destroy_context
|
||||||
from ipalib.rpc import (xml_dumps, xml_loads,
|
from ipalib.rpc import (xml_dumps, xml_loads,
|
||||||
json_encode_binary, json_decode_binary)
|
json_encode_binary, json_decode_binary)
|
||||||
@ -944,37 +944,12 @@ class login_password(Backend, KerberosSession, HTTP_Status):
|
|||||||
|
|
||||||
# Get the ccache we'll use and attempt to get credentials in it with user,password
|
# Get the ccache we'll use and attempt to get credentials in it with user,password
|
||||||
ipa_ccache_name = get_ipa_ccache_name()
|
ipa_ccache_name = get_ipa_ccache_name()
|
||||||
reason = 'invalid-password'
|
|
||||||
try:
|
try:
|
||||||
self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
|
self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
|
||||||
except InvalidSessionPassword, e:
|
except PasswordExpired as e:
|
||||||
# Ok, now why is this bad. Is the password simply bad or is the
|
return self.unauthorized(environ, start_response, str(e), 'password-expired')
|
||||||
# password expired?
|
except InvalidSessionPassword as e:
|
||||||
try:
|
return self.unauthorized(environ, start_response, str(e), 'invalid-password')
|
||||||
dn = DN(('uid', user),
|
|
||||||
self.api.env.container_user,
|
|
||||||
self.api.env.basedn)
|
|
||||||
conn = ldap2(shared_instance=False,
|
|
||||||
ldap_uri=self.api.env.ldap_uri)
|
|
||||||
conn.connect(bind_dn=dn, bind_pw=password)
|
|
||||||
|
|
||||||
# password is ok, must be expired, lets double-check
|
|
||||||
entry_attrs = conn.get_entry(dn,
|
|
||||||
['krbpasswordexpiration'])
|
|
||||||
if 'krbpasswordexpiration' in entry_attrs:
|
|
||||||
expiration = entry_attrs['krbpasswordexpiration'][0]
|
|
||||||
if expiration <= datetime.datetime.utcnow():
|
|
||||||
reason = 'password-expired'
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
# It doesn't really matter how we got here but the user's
|
|
||||||
# password is not accepted or the user is unknown.
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
if conn.isconnected():
|
|
||||||
conn.destroy_connection()
|
|
||||||
|
|
||||||
return self.unauthorized(environ, start_response, str(e), reason)
|
|
||||||
|
|
||||||
return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
|
return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
|
||||||
|
|
||||||
@ -1001,7 +976,8 @@ class login_password(Backend, KerberosSession, HTTP_Status):
|
|||||||
|
|
||||||
(stdout, stderr, returncode) = ipautil.run(
|
(stdout, stderr, returncode) = ipautil.run(
|
||||||
[paths.KINIT, principal, '-T', armor_path],
|
[paths.KINIT, principal, '-T', armor_path],
|
||||||
env={'KRB5CCNAME': ccache_name}, stdin=password, raiseonerr=False)
|
env={'KRB5CCNAME': ccache_name, 'LC_ALL': 'C'},
|
||||||
|
stdin=password, raiseonerr=False)
|
||||||
|
|
||||||
self.debug('kinit: principal=%s returncode=%s, stderr="%s"',
|
self.debug('kinit: principal=%s returncode=%s, stderr="%s"',
|
||||||
principal, returncode, stderr)
|
principal, returncode, stderr)
|
||||||
@ -1013,6 +989,8 @@ class login_password(Backend, KerberosSession, HTTP_Status):
|
|||||||
raiseonerr=False)
|
raiseonerr=False)
|
||||||
|
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
|
if stderr.strip() == 'kinit: Cannot read password while getting initial credentials':
|
||||||
|
raise PasswordExpired(principal=principal, message=unicode(stderr))
|
||||||
raise InvalidSessionPassword(principal=principal, message=unicode(stderr))
|
raise InvalidSessionPassword(principal=principal, message=unicode(stderr))
|
||||||
|
|
||||||
class change_password(Backend, HTTP_Status):
|
class change_password(Backend, HTTP_Status):
|
||||||
|
@ -86,10 +86,8 @@ class test_ipagetkeytab(cmdline_test):
|
|||||||
"-k", self.keytabname,
|
"-k", self.keytabname,
|
||||||
]
|
]
|
||||||
(out, err, rc) = ipautil.run(new_args, stdin=None, raiseonerr=False)
|
(out, err, rc) = ipautil.run(new_args, stdin=None, raiseonerr=False)
|
||||||
assert err == (
|
assert 'Failed to parse result: PrincipalName not found.\n' in err, err
|
||||||
'Failed to parse result! PrincipalName not found.\n\n'
|
assert rc > 0, rc
|
||||||
'Failed to get keytab\n'
|
|
||||||
), err
|
|
||||||
|
|
||||||
def test_2_run(self):
|
def test_2_run(self):
|
||||||
"""
|
"""
|
||||||
|
@ -315,15 +315,7 @@ def configure_dns_for_trust(master, ad):
|
|||||||
|
|
||||||
kinit_admin(master)
|
kinit_admin(master)
|
||||||
|
|
||||||
if is_subdomain(master.domain.name, ad.domain.name):
|
if is_subdomain(ad.domain.name, master.domain.name):
|
||||||
master.run_command(['ipa', 'dnszone-add', ad.domain.name,
|
|
||||||
'--name-server', ad.hostname,
|
|
||||||
'--admin-email', 'hostmaster@%s' % ad.domain.name,
|
|
||||||
'--forwarder', ad.ip,
|
|
||||||
'--forward-policy', 'only',
|
|
||||||
'--ip-address', ad.ip,
|
|
||||||
'--force'])
|
|
||||||
elif is_subdomain(ad.domain.name, master.domain.name):
|
|
||||||
master.run_command(['ipa', 'dnsrecord-add', master.domain.name,
|
master.run_command(['ipa', 'dnsrecord-add', master.domain.name,
|
||||||
'%s.%s' % (ad.shortname, ad.netbios),
|
'%s.%s' % (ad.shortname, ad.netbios),
|
||||||
'--a-ip-address', ad.ip])
|
'--a-ip-address', ad.ip])
|
||||||
@ -336,13 +328,10 @@ def configure_dns_for_trust(master, ad):
|
|||||||
master.run_command(['ipa', 'dnszone-mod', master.domain.name,
|
master.run_command(['ipa', 'dnszone-mod', master.domain.name,
|
||||||
'--allow-transfer', ad.ip])
|
'--allow-transfer', ad.ip])
|
||||||
else:
|
else:
|
||||||
master.run_command(['ipa', 'dnszone-add', ad.domain.name,
|
master.run_command(['ipa', 'dnsforwardzone-add', ad.domain.name,
|
||||||
'--name-server', ad.hostname,
|
|
||||||
'--admin-email', 'hostmaster@%s' % ad.domain.name,
|
|
||||||
'--forwarder', ad.ip,
|
'--forwarder', ad.ip,
|
||||||
'--forward-policy', 'only',
|
'--forward-policy', 'only',
|
||||||
'--ip-address', ad.ip,
|
])
|
||||||
'--force'])
|
|
||||||
|
|
||||||
|
|
||||||
def establish_trust_with_ad(master, ad, extra_args=()):
|
def establish_trust_with_ad(master, ad, extra_args=()):
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import nose
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ipatests.test_integration.base import IntegrationTest
|
from ipatests.test_integration.base import IntegrationTest
|
||||||
@ -29,6 +30,7 @@ class ADTrustBase(IntegrationTest):
|
|||||||
|
|
||||||
topology = 'line'
|
topology = 'line'
|
||||||
num_ad_domains = 1
|
num_ad_domains = 1
|
||||||
|
optional_extra_roles = ['ad_subdomain']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def install(cls):
|
def install(cls):
|
||||||
@ -38,6 +40,14 @@ class ADTrustBase(IntegrationTest):
|
|||||||
cls.check_sid_generation()
|
cls.check_sid_generation()
|
||||||
cls.configure_dns_and_time()
|
cls.configure_dns_and_time()
|
||||||
|
|
||||||
|
# Determine whether the subdomain AD is available
|
||||||
|
try:
|
||||||
|
child_ad = cls.host_by_role(cls.optional_extra_roles[0])
|
||||||
|
cls.ad_subdomain = '.'.join(
|
||||||
|
child_ad.hostname.split('.')[1:])
|
||||||
|
except LookupError:
|
||||||
|
cls.ad_subdomain = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def install_adtrust(cls):
|
def install_adtrust(cls):
|
||||||
"""Test adtrust support installation"""
|
"""Test adtrust support installation"""
|
||||||
@ -64,16 +74,32 @@ class ADTrustBase(IntegrationTest):
|
|||||||
tasks.configure_dns_for_trust(cls.master, cls.ad)
|
tasks.configure_dns_for_trust(cls.master, cls.ad)
|
||||||
tasks.sync_time(cls.master, cls.ad)
|
tasks.sync_time(cls.master, cls.ad)
|
||||||
|
|
||||||
|
|
||||||
class TestBasicADTrust(ADTrustBase):
|
|
||||||
"""Basic Integration test for Active Directory"""
|
|
||||||
|
|
||||||
def test_establish_trust(self):
|
def test_establish_trust(self):
|
||||||
"""Tests establishing trust with Active Directory"""
|
"""Tests establishing trust with Active Directory"""
|
||||||
|
|
||||||
tasks.establish_trust_with_ad(self.master, self.ad,
|
tasks.establish_trust_with_ad(self.master, self.ad,
|
||||||
extra_args=['--range-type', 'ipa-ad-trust'])
|
extra_args=['--range-type', 'ipa-ad-trust'])
|
||||||
|
|
||||||
|
def test_all_trustdomains_found(self):
|
||||||
|
"""
|
||||||
|
Tests that all trustdomains can be found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.ad_subdomain is None:
|
||||||
|
raise nose.SkipTest('AD subdomain is not available.')
|
||||||
|
|
||||||
|
result = self.master.run_command(['ipa',
|
||||||
|
'trustdomain-find',
|
||||||
|
self.ad.domain.name])
|
||||||
|
|
||||||
|
# Check that both trustdomains appear in the result
|
||||||
|
assert self.ad.domain.name in result.stdout_text
|
||||||
|
assert self.ad_subdomain in result.stdout_text
|
||||||
|
|
||||||
|
|
||||||
|
class TestBasicADTrust(ADTrustBase):
|
||||||
|
"""Basic Integration test for Active Directory"""
|
||||||
|
|
||||||
def test_range_properties_in_nonposix_trust(self):
|
def test_range_properties_in_nonposix_trust(self):
|
||||||
"""Check the properties of the created range"""
|
"""Check the properties of the created range"""
|
||||||
|
|
||||||
@ -111,7 +137,7 @@ class TestBasicADTrust(ADTrustBase):
|
|||||||
class TestPosixADTrust(ADTrustBase):
|
class TestPosixADTrust(ADTrustBase):
|
||||||
"""Integration test for Active Directory with POSIX support"""
|
"""Integration test for Active Directory with POSIX support"""
|
||||||
|
|
||||||
def test_establish_trust_with_posix_attributes(self):
|
def test_establish_trust(self):
|
||||||
# Not specifying the --range-type directly, it should be detected
|
# Not specifying the --range-type directly, it should be detected
|
||||||
tasks.establish_trust_with_ad(self.master, self.ad)
|
tasks.establish_trust_with_ad(self.master, self.ad)
|
||||||
|
|
||||||
|
@ -33,11 +33,11 @@ DATA = {
|
|||||||
('textbox', 'aciname', PKEY),
|
('textbox', 'aciname', PKEY),
|
||||||
('combobox', 'group', 'editors'),
|
('combobox', 'group', 'editors'),
|
||||||
('combobox', 'memberof', 'ipausers'),
|
('combobox', 'memberof', 'ipausers'),
|
||||||
('table', 'attrs', 'audio'),
|
('checkbox', 'attrs', 'audio'),
|
||||||
('table', 'attrs', 'businesscategory'),
|
('checkbox', 'attrs', 'businesscategory'),
|
||||||
],
|
],
|
||||||
'mod': [
|
'mod': [
|
||||||
('table', 'attrs', 'businesscategory'),
|
('checkbox', 'attrs', 'businesscategory'),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +61,11 @@ PERMISSION_DATA = {
|
|||||||
('checkbox', 'ipapermright', 'write'),
|
('checkbox', 'ipapermright', 'write'),
|
||||||
('checkbox', 'ipapermright', 'read'),
|
('checkbox', 'ipapermright', 'read'),
|
||||||
('selectbox', 'type', 'user'),
|
('selectbox', 'type', 'user'),
|
||||||
('table', 'attrs', 'audio'),
|
('checkbox', 'attrs', 'audio'),
|
||||||
('table', 'attrs', 'cn'),
|
('checkbox', 'attrs', 'cn'),
|
||||||
],
|
],
|
||||||
'mod': [
|
'mod': [
|
||||||
('table', 'attrs', 'carlicense'),
|
('checkbox', 'attrs', 'carlicense'),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ DATA = {
|
|||||||
'pkey': PKEY,
|
'pkey': PKEY,
|
||||||
'add': [
|
'add': [
|
||||||
('textbox', 'aciname', PKEY),
|
('textbox', 'aciname', PKEY),
|
||||||
('table', 'attrs', 'audio'),
|
('checkbox', 'attrs', 'audio'),
|
||||||
('table', 'attrs', 'businesscategory'),
|
('checkbox', 'attrs', 'businesscategory'),
|
||||||
],
|
],
|
||||||
'mod': [
|
'mod': [
|
||||||
('table', 'attrs', 'businesscategory'),
|
('checkbox', 'attrs', 'businesscategory'),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,9 +368,9 @@ class UI_driver(object):
|
|||||||
self.wait_for_request(n=2)
|
self.wait_for_request(n=2)
|
||||||
|
|
||||||
# reset password if needed
|
# reset password if needed
|
||||||
newpw_tb = self.find("//input[@type='password'][@name='new_password']", 'xpath', auth, strict=True)
|
newpw_tb = self.find("//input[@type='password'][@name='new_password']", 'xpath', auth)
|
||||||
verify_tb = self.find("//input[@type='password'][@name='verify_password']", 'xpath', auth, strict=True)
|
verify_tb = self.find("//input[@type='password'][@name='verify_password']", 'xpath', auth)
|
||||||
if newpw_tb.is_displayed():
|
if newpw_tb and newpw_tb.is_displayed():
|
||||||
newpw_tb.send_keys(new_password)
|
newpw_tb.send_keys(new_password)
|
||||||
verify_tb.send_keys(new_password)
|
verify_tb.send_keys(new_password)
|
||||||
verify_tb.send_keys(Keys.RETURN)
|
verify_tb.send_keys(Keys.RETURN)
|
||||||
@ -609,7 +609,7 @@ class UI_driver(object):
|
|||||||
if not dialog:
|
if not dialog:
|
||||||
dialog = self.get_dialog(strict=True)
|
dialog = self.get_dialog(strict=True)
|
||||||
|
|
||||||
s = ".rcue-dialog-buttons button[name=%s]" % name
|
s = ".rcue-dialog-buttons button[name='%s']" % name
|
||||||
self._button_click(s, dialog, name)
|
self._button_click(s, dialog, name)
|
||||||
|
|
||||||
def action_button_click(self, name, parent):
|
def action_button_click(self, name, parent):
|
||||||
@ -634,10 +634,8 @@ class UI_driver(object):
|
|||||||
|
|
||||||
def _button_click(self, selector, parent, name=''):
|
def _button_click(self, selector, parent, name=''):
|
||||||
btn = self.find(selector, By.CSS_SELECTOR, parent, strict=True)
|
btn = self.find(selector, By.CSS_SELECTOR, parent, strict=True)
|
||||||
|
ActionChains(self.driver).move_to_element(btn).perform()
|
||||||
disabled = 'ui-state-disabled' in btn.get_attribute("class").split() or \
|
disabled = btn.get_attribute("disabled")
|
||||||
btn.get_attribute("disabled")
|
|
||||||
|
|
||||||
assert btn.is_displayed(), 'Button is not displayed: %s' % name
|
assert btn.is_displayed(), 'Button is not displayed: %s' % name
|
||||||
assert not disabled, 'Invalid button state: disabled. Button: %s' % name
|
assert not disabled, 'Invalid button state: disabled. Button: %s' % name
|
||||||
btn.click()
|
btn.click()
|
||||||
@ -940,13 +938,18 @@ class UI_driver(object):
|
|||||||
parent = self.get_form()
|
parent = self.get_form()
|
||||||
|
|
||||||
s = self.get_table_selector(table_name)
|
s = self.get_table_selector(table_name)
|
||||||
s += " tbody td input[value='%s']+label" % pkey
|
input_s = s + " tbody td input[value='%s']" % pkey
|
||||||
label = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
checkbox = self.find(input_s, By.CSS_SELECTOR, parent, strict=True)
|
||||||
|
checkbox_id = checkbox.get_attribute('id')
|
||||||
|
label_s = s + " tbody td label[for='%s']" % checkbox_id
|
||||||
|
print label_s
|
||||||
|
label = self.find(label_s, By.CSS_SELECTOR, parent, strict=True)
|
||||||
try:
|
try:
|
||||||
ActionChains(self.driver).move_to_element(label).click().perform()
|
ActionChains(self.driver).move_to_element(label).click().perform()
|
||||||
except WebDriverException as e:
|
except WebDriverException as e:
|
||||||
assert False, 'Can\'t click on checkbox label: %s \n%s' % (s, e)
|
assert False, 'Can\'t click on checkbox label: %s \n%s' % (s, e)
|
||||||
|
self.wait()
|
||||||
|
assert checkbox.is_selected(), 'Record was not checked: %s' % input_s
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def get_record_value(self, pkey, column, parent=None, table_name=None):
|
def get_record_value(self, pkey, column, parent=None, table_name=None):
|
||||||
@ -1011,7 +1014,7 @@ class UI_driver(object):
|
|||||||
if table_name and parent:
|
if table_name and parent:
|
||||||
s = self.get_table_selector(table_name)
|
s = self.get_table_selector(table_name)
|
||||||
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
||||||
self.action_button_click('remove', table)
|
self.button_click('remove', table)
|
||||||
else:
|
else:
|
||||||
self.facet_button_click('remove')
|
self.facet_button_click('remove')
|
||||||
if fields:
|
if fields:
|
||||||
@ -1323,7 +1326,7 @@ class UI_driver(object):
|
|||||||
parent = self.get_form()
|
parent = self.get_form()
|
||||||
s = self.get_table_selector(name)
|
s = self.get_table_selector(name)
|
||||||
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
||||||
s = "a[name=%s].button" % 'add'
|
s = ".btn[name=%s]" % 'add'
|
||||||
btn = self.find(s, By.CSS_SELECTOR, table, strict=True)
|
btn = self.find(s, By.CSS_SELECTOR, table, strict=True)
|
||||||
btn.click()
|
btn.click()
|
||||||
self.wait()
|
self.wait()
|
||||||
@ -1339,6 +1342,7 @@ class UI_driver(object):
|
|||||||
self.switch_to_facet(facet)
|
self.switch_to_facet(facet)
|
||||||
|
|
||||||
self.facet_button_click('add')
|
self.facet_button_click('add')
|
||||||
|
self.wait()
|
||||||
self.wait_for_request()
|
self.wait_for_request()
|
||||||
|
|
||||||
for key in pkeys:
|
for key in pkeys:
|
||||||
@ -1364,7 +1368,7 @@ class UI_driver(object):
|
|||||||
s = self.get_table_selector(table_name)
|
s = self.get_table_selector(table_name)
|
||||||
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
table = self.find(s, By.CSS_SELECTOR, parent, strict=True)
|
||||||
|
|
||||||
s = "a[name=%s].button" % 'add'
|
s = "button[name='%s']" % 'add'
|
||||||
btn = self.find(s, By.CSS_SELECTOR, table, strict=True)
|
btn = self.find(s, By.CSS_SELECTOR, table, strict=True)
|
||||||
btn.click()
|
btn.click()
|
||||||
self.wait_for_request(0.4)
|
self.wait_for_request(0.4)
|
||||||
@ -1372,6 +1376,7 @@ class UI_driver(object):
|
|||||||
for key in pkeys:
|
for key in pkeys:
|
||||||
self.select_record(key, table_name='available')
|
self.select_record(key, table_name='available')
|
||||||
self.button_click('add')
|
self.button_click('add')
|
||||||
|
self.wait()
|
||||||
|
|
||||||
self.dialog_button_click('add')
|
self.dialog_button_click('add')
|
||||||
self.wait_for_request(n=2)
|
self.wait_for_request(n=2)
|
||||||
@ -1550,21 +1555,6 @@ class UI_driver(object):
|
|||||||
"""
|
"""
|
||||||
assert expected == current, "Rows don't match. Expected: %d, Got: %d" % (expected, current)
|
assert expected == current, "Rows don't match. Expected: %d, Got: %d" % (expected, current)
|
||||||
|
|
||||||
def assert_action_button_enabled(self, name, context_selector=None, enabled=True):
|
|
||||||
"""
|
|
||||||
Assert that action-button is enabled or disabled
|
|
||||||
"""
|
|
||||||
s = ""
|
|
||||||
if context_selector:
|
|
||||||
s = context_selector
|
|
||||||
s += "a[name=%s]" % name
|
|
||||||
facet = self.get_facet()
|
|
||||||
btn = self.find(s, By.CSS_SELECTOR, facet, strict=True)
|
|
||||||
cls = 'action-button-disabled'
|
|
||||||
valid = enabled ^ self.has_class(btn, cls)
|
|
||||||
assert btn.is_displayed(), 'Button is not displayed'
|
|
||||||
assert valid, 'Button has incorrect enabled state.'
|
|
||||||
|
|
||||||
def assert_button_enabled(self, name, context_selector=None, enabled=True):
|
def assert_button_enabled(self, name, context_selector=None, enabled=True):
|
||||||
"""
|
"""
|
||||||
Assert that button is enabled or disabled (expects that element will be
|
Assert that button is enabled or disabled (expects that element will be
|
||||||
@ -1591,7 +1581,7 @@ class UI_driver(object):
|
|||||||
Assert that button in table is enabled/disabled
|
Assert that button in table is enabled/disabled
|
||||||
"""
|
"""
|
||||||
s = "table[name='%s'] " % table_name
|
s = "table[name='%s'] " % table_name
|
||||||
self.assert_action_button_enabled(name, s, enabled)
|
self.assert_button_enabled(name, s, enabled)
|
||||||
|
|
||||||
def assert_facet(self, entity, facet=None):
|
def assert_facet(self, entity, facet=None):
|
||||||
"""
|
"""
|
||||||
|
@ -21,8 +21,13 @@
|
|||||||
Test the `ipalib.plugins.baseldap` module.
|
Test the `ipalib.plugins.baseldap` module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ldap
|
||||||
|
|
||||||
|
from ipapython.dn import DN
|
||||||
|
from ipapython import ipaldap
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
from ipalib.plugins import baseldap
|
from ipalib.plugins import baseldap
|
||||||
|
from ipatests.util import assert_deepequal
|
||||||
|
|
||||||
|
|
||||||
def test_exc_wrapper():
|
def test_exc_wrapper():
|
||||||
@ -157,3 +162,47 @@ def test_exc_callback_registration():
|
|||||||
messages = []
|
messages = []
|
||||||
subclass_instance.test_fail()
|
subclass_instance.test_fail()
|
||||||
assert messages == ['Base exc_callback', 'Subclass registered callback']
|
assert messages == ['Base exc_callback', 'Subclass registered callback']
|
||||||
|
|
||||||
|
|
||||||
|
def test_entry_to_dict():
|
||||||
|
class FakeAttributeType(object):
|
||||||
|
def __init__(self, name, syntax):
|
||||||
|
self.names = (name,)
|
||||||
|
self.syntax = syntax
|
||||||
|
|
||||||
|
class FakeSchema(object):
|
||||||
|
def get_obj(self, type, name):
|
||||||
|
if type != ldap.schema.AttributeType:
|
||||||
|
return
|
||||||
|
if name == 'binaryattr':
|
||||||
|
return FakeAttributeType(name, '1.3.6.1.4.1.1466.115.121.1.40')
|
||||||
|
elif name == 'textattr':
|
||||||
|
return FakeAttributeType(name, '1.3.6.1.4.1.1466.115.121.1.15')
|
||||||
|
elif name == 'dnattr':
|
||||||
|
return FakeAttributeType(name, '1.3.6.1.4.1.1466.115.121.1.12')
|
||||||
|
|
||||||
|
class FakeIPASimpleLDAPObject(ipaldap.IPASimpleLDAPObject):
|
||||||
|
def __init__(self):
|
||||||
|
super(FakeIPASimpleLDAPObject, self).__init__('ldap://test', False)
|
||||||
|
self._schema = FakeSchema()
|
||||||
|
self._has_schema = True
|
||||||
|
|
||||||
|
conn = FakeIPASimpleLDAPObject()
|
||||||
|
rights = {'nothing': 'is'}
|
||||||
|
|
||||||
|
entry = ipaldap.LDAPEntry(
|
||||||
|
conn,
|
||||||
|
DN('cn=test'),
|
||||||
|
textattr=[u'text'],
|
||||||
|
dnattr=[DN('cn=test')],
|
||||||
|
binaryattr=['\xffabcd'],
|
||||||
|
attributelevelrights=rights)
|
||||||
|
the_dict = {
|
||||||
|
u'dn': u'cn=test',
|
||||||
|
u'textattr': [u'text'],
|
||||||
|
u'dnattr': [u'cn=test'],
|
||||||
|
u'binaryattr': ['\xffabcd'],
|
||||||
|
u'attributelevelrights': rights}
|
||||||
|
assert_deepequal(
|
||||||
|
baseldap.entry_to_dict(entry, all=True, raw=True),
|
||||||
|
the_dict)
|
||||||
|
@ -263,6 +263,7 @@ zone_findtest_forward = u'forward.find.test.'
|
|||||||
zone_findtest_forward_dnsname = DNSName(zone_findtest_forward)
|
zone_findtest_forward_dnsname = DNSName(zone_findtest_forward)
|
||||||
zone_findtest_forward_dn = DN(('idnsname', zone_findtest_forward), api.env.container_dns, api.env.basedn)
|
zone_findtest_forward_dn = DN(('idnsname', zone_findtest_forward), api.env.container_dns, api.env.basedn)
|
||||||
|
|
||||||
|
zone_fw_wildcard = u'*.wildcardforwardzone.test.'
|
||||||
|
|
||||||
class test_dns(Declarative):
|
class test_dns(Declarative):
|
||||||
|
|
||||||
@ -289,7 +290,8 @@ class test_dns(Declarative):
|
|||||||
revzone3_classless1, revzone3_classless2,
|
revzone3_classless1, revzone3_classless2,
|
||||||
idnzone1, revidnzone1, zone_findtest_master],
|
idnzone1, revidnzone1, zone_findtest_master],
|
||||||
{'continue': True}),
|
{'continue': True}),
|
||||||
('dnsforwardzone_del', [fwzone1, zone_findtest_forward],
|
('dnsforwardzone_del', [fwzone1, zone_findtest_forward,
|
||||||
|
zone_fw_wildcard],
|
||||||
{'continue': True}),
|
{'continue': True}),
|
||||||
('dnsconfig_mod', [], {'idnsforwarders' : None,
|
('dnsconfig_mod', [], {'idnsforwarders' : None,
|
||||||
'idnsforwardpolicy' : None,
|
'idnsforwardpolicy' : None,
|
||||||
@ -1378,10 +1380,10 @@ class test_dns(Declarative):
|
|||||||
|
|
||||||
|
|
||||||
dict(
|
dict(
|
||||||
desc='Delete record %r in zone %r' % (tlsa, zone1),
|
desc='Remove record using dnsrecord-mod %r in zone %r' % (tlsa, zone1),
|
||||||
command=('dnsrecord_del', [zone1, tlsa], {'del_all': True}),
|
command=('dnsrecord_mod', [zone1, tlsa], {'tlsarecord': ''}),
|
||||||
expected={
|
expected={
|
||||||
'value': [tlsa_dnsname],
|
'value': tlsa_dnsname,
|
||||||
'summary': u'Deleted record "%s"' % tlsa,
|
'summary': u'Deleted record "%s"' % tlsa,
|
||||||
'result': {'failed': []},
|
'result': {'failed': []},
|
||||||
},
|
},
|
||||||
@ -2735,6 +2737,39 @@ class test_dns(Declarative):
|
|||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to add NS record to wildcard owner %r in zone %r' % (wildcard_rec1, zone1),
|
||||||
|
command=('dnsrecord_add', [zone1, wildcard_rec1], {'nsrecord': zone2_ns, 'force': True}),
|
||||||
|
expected=errors.ValidationError(
|
||||||
|
name='idnsname',
|
||||||
|
error=(u'owner of DNAME, DS, NS records '
|
||||||
|
'should not be a wildcard domain name (RFC 4592 section 4)')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to add DNAME record to wildcard owner %r in zone %r' % (wildcard_rec1, zone1),
|
||||||
|
command=('dnsrecord_add', [zone1, wildcard_rec1], {'dnamerecord': u'dname.test.'}),
|
||||||
|
expected=errors.ValidationError(
|
||||||
|
name='idnsname',
|
||||||
|
error=(u'owner of DNAME, DS, NS records '
|
||||||
|
'should not be a wildcard domain name (RFC 4592 section 4)')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to add DS record to wildcard owner %r in zone %r' % (wildcard_rec1, zone1),
|
||||||
|
command=('dnsrecord_add', [zone1, wildcard_rec1], {'dsrecord': u'0 0 0 00'}),
|
||||||
|
expected=errors.ValidationError(
|
||||||
|
name='idnsname',
|
||||||
|
error=(u'owner of DNAME, DS, NS records '
|
||||||
|
'should not be a wildcard domain name (RFC 4592 section 4)')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
dict(
|
dict(
|
||||||
desc='Add A denormalized record in zone %r' % (idnzone1),
|
desc='Add A denormalized record in zone %r' % (idnzone1),
|
||||||
command=('dnsrecord_add', [idnzone1, u'gro\xdf'], {'arecord': u'172.16.0.1'}),
|
command=('dnsrecord_add', [idnzone1, u'gro\xdf'], {'arecord': u'172.16.0.1'}),
|
||||||
@ -2743,6 +2778,16 @@ class test_dns(Declarative):
|
|||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to create forward zone %r with wildcard domain name' % zone_fw_wildcard,
|
||||||
|
command=(
|
||||||
|
'dnsforwardzone_add', [zone_fw_wildcard], {'idnsforwardpolicy': u'none'}
|
||||||
|
),
|
||||||
|
expected=errors.ValidationError(name='name',
|
||||||
|
error=u'should not be a wildcard domain name (RFC 4592 section 4)')
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
dict(
|
dict(
|
||||||
desc='Try to create forward zone %r without forwarders with default "(first)" policy' % fwzone1,
|
desc='Try to create forward zone %r without forwarders with default "(first)" policy' % fwzone1,
|
||||||
command=(
|
command=(
|
||||||
|
@ -1009,5 +1009,72 @@ class test_group(Declarative):
|
|||||||
value=[user1],
|
value=[user1],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
class test_group_remove_group_from_protected_group(Declarative):
|
||||||
|
cleanup_commands = [
|
||||||
|
('group_del', [group1], {}),
|
||||||
|
]
|
||||||
|
tests = [
|
||||||
|
# Test scenario from ticket #4448
|
||||||
|
# https://fedorahosted.org/freeipa/ticket/4448
|
||||||
|
dict(
|
||||||
|
desc='Add group %s' % group1,
|
||||||
|
command=('group_add', [group1], dict(description=u'Test desc 1')),
|
||||||
|
expected=dict(
|
||||||
|
value=group1,
|
||||||
|
summary=u'Added group "%s"' % group1,
|
||||||
|
result=dict(
|
||||||
|
cn=[group1],
|
||||||
|
description=[u'Test desc 1'],
|
||||||
|
objectclass=objectclasses.posixgroup,
|
||||||
|
gidnumber=[fuzzy_digits],
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
dn=get_group_dn(group1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Add %s group to admins group' % group1,
|
||||||
|
command=('group_add_member', [u'admins'], dict(group=group1)),
|
||||||
|
expected=dict(
|
||||||
|
completed=1,
|
||||||
|
failed=dict(
|
||||||
|
member=dict(
|
||||||
|
group=tuple(),
|
||||||
|
user=tuple(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
result=dict(
|
||||||
|
dn=get_group_dn('admins'),
|
||||||
|
member_user=[u'admin'],
|
||||||
|
member_group=[group1],
|
||||||
|
gidnumber=[fuzzy_digits],
|
||||||
|
cn=[u'admins'],
|
||||||
|
description=[u'Account administrators group'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Remove %s group from admins group' % group1,
|
||||||
|
command=('group_remove_member', [u'admins'], dict(group=group1)),
|
||||||
|
expected=dict(
|
||||||
|
completed=1,
|
||||||
|
failed=dict(
|
||||||
|
member=dict(
|
||||||
|
group=tuple(),
|
||||||
|
user=tuple(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
result=dict(
|
||||||
|
dn=get_group_dn(u'admins'),
|
||||||
|
cn=[u'admins'],
|
||||||
|
gidnumber=[fuzzy_digits],
|
||||||
|
member_user=[u'admin'],
|
||||||
|
description=[u'Account administrators group'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -27,6 +27,7 @@ import tempfile
|
|||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipalib import api, errors, x509
|
from ipalib import api, errors, x509
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
|
from ipapython.dnsutil import DNSName
|
||||||
from nose.tools import raises, assert_raises
|
from nose.tools import raises, assert_raises
|
||||||
from nose.plugins.skip import SkipTest
|
from nose.plugins.skip import SkipTest
|
||||||
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, XMLRPC_test,
|
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, XMLRPC_test,
|
||||||
@ -56,6 +57,82 @@ dn4 = DN(('fqdn',fqdn4),('cn','computers'),('cn','accounts'),
|
|||||||
api.env.basedn)
|
api.env.basedn)
|
||||||
invalidfqdn1 = u'foo_bar.lab.%s' % api.env.domain
|
invalidfqdn1 = u'foo_bar.lab.%s' % api.env.domain
|
||||||
|
|
||||||
|
# DNS integration tests
|
||||||
|
dnszone = u'zone-ipv6host.test'
|
||||||
|
dnszone_absolute = dnszone + '.'
|
||||||
|
dnszone_dnsname = DNSName(dnszone_absolute)
|
||||||
|
dnszone_dn = DN(('idnsname', dnszone_absolute), api.env.container_dns, api.env.basedn)
|
||||||
|
dnszone_ns = u'ns1.%s' % dnszone_absolute
|
||||||
|
dnszone_ns_dnsname = DNSName(dnszone_ns)
|
||||||
|
dnszone_rname = u'root.%s' % dnszone_absolute
|
||||||
|
dnszone_rname_dnsname = DNSName(dnszone_rname)
|
||||||
|
dnszone_ip = u'172.16.29.1'
|
||||||
|
|
||||||
|
revzone = u'29.16.172.in-addr.arpa.'
|
||||||
|
revzone_dnsname = DNSName(revzone)
|
||||||
|
revzone_ip = u'172.16.29.0'
|
||||||
|
revzone_ipprefix = u'172.16.29.'
|
||||||
|
revzone_dn = DN(('idnsname', revzone), api.env.container_dns, api.env.basedn)
|
||||||
|
|
||||||
|
revipv6zone = u'0.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.'
|
||||||
|
revipv6zone_dnsname = DNSName(revipv6zone)
|
||||||
|
revipv6zone_ip = u'2001:db8:1::'
|
||||||
|
revipv6zone_ipprefix = u'2001:db8:1:'
|
||||||
|
revipv6zone_dn = DN(('idnsname', revipv6zone), api.env.container_dns, api.env.basedn)
|
||||||
|
|
||||||
|
arec = u'172.16.29.22'
|
||||||
|
aaaarec = u'2001:db8:1::beef'
|
||||||
|
|
||||||
|
arec2 = u'172.16.29.33'
|
||||||
|
aaaarec2 = u'2001:db8:1::dead'
|
||||||
|
|
||||||
|
ipv6only = u'testipv6onlyhost'
|
||||||
|
ipv6only_dnsname = DNSName(ipv6only)
|
||||||
|
ipv6only_dn = DN(('idnsname', ipv6only), dnszone_dn)
|
||||||
|
ipv6only_host_fqdn = u'%s.%s' % (ipv6only, dnszone)
|
||||||
|
ipv6only_host_dn = DN(('fqdn',ipv6only_host_fqdn),('cn','computers'),('cn','accounts'),
|
||||||
|
api.env.basedn)
|
||||||
|
|
||||||
|
ipv4only = u'testipv4onlyhost'
|
||||||
|
ipv4only_dnsname = DNSName(ipv4only)
|
||||||
|
ipv4only_dn = DN(('idnsname', ipv4only), dnszone_dn)
|
||||||
|
ipv4only_host_fqdn = u'%s.%s' % (ipv4only, dnszone)
|
||||||
|
ipv4only_host_dn = DN(('fqdn',ipv4only_host_fqdn),('cn','computers'),('cn','accounts'),
|
||||||
|
api.env.basedn)
|
||||||
|
|
||||||
|
ipv46both = u'testipv4and6host'
|
||||||
|
ipv46both_dnsname = DNSName(ipv46both)
|
||||||
|
ipv46both_dn = DN(('idnsname', ipv46both), dnszone_dn)
|
||||||
|
ipv46both_host_fqdn = u'%s.%s' % (ipv46both, dnszone)
|
||||||
|
ipv46both_host_dn = DN(('fqdn',ipv46both_host_fqdn),('cn','computers'),('cn','accounts'),
|
||||||
|
api.env.basedn)
|
||||||
|
|
||||||
|
ipv4_fromip = u'withipv4addr'
|
||||||
|
ipv4_fromip_ip = u'172.16.29.40'
|
||||||
|
ipv4_fromip_arec = ipv4_fromip_ip
|
||||||
|
ipv4_fromip_dnsname = DNSName(ipv4_fromip)
|
||||||
|
ipv4_fromip_dn = DN(('idnsname', ipv4_fromip), dnszone_dn)
|
||||||
|
ipv4_fromip_host_fqdn = u'%s.%s' % (ipv4_fromip, dnszone)
|
||||||
|
ipv4_fromip_host_dn = DN(('fqdn',ipv4_fromip_host_fqdn),('cn','computers'),('cn','accounts'),
|
||||||
|
api.env.basedn)
|
||||||
|
ipv4_fromip_ptr = u'40'
|
||||||
|
ipv4_fromip_ptrrec = ipv4_fromip_host_fqdn + '.'
|
||||||
|
ipv4_fromip_ptr_dnsname = DNSName(ipv4_fromip_ptr)
|
||||||
|
ipv4_fromip_ptr_dn = DN(('idnsname', ipv4_fromip_ptr), revzone_dn)
|
||||||
|
|
||||||
|
ipv6_fromip = u'withipv6addr'
|
||||||
|
ipv6_fromip_ipv6 = u'2001:db8:1::9'
|
||||||
|
ipv6_fromip_aaaarec = ipv6_fromip_ipv6
|
||||||
|
ipv6_fromip_dnsname = DNSName(ipv6_fromip)
|
||||||
|
ipv6_fromip_dn = DN(('idnsname', ipv6_fromip), dnszone_dn)
|
||||||
|
ipv6_fromip_host_fqdn = u'%s.%s' % (ipv6_fromip, dnszone)
|
||||||
|
ipv6_fromip_host_dn = DN(('fqdn',ipv6_fromip_host_fqdn),('cn','computers'),('cn','accounts'),
|
||||||
|
api.env.basedn)
|
||||||
|
ipv6_fromip_ptr = u'9.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0'
|
||||||
|
ipv6_fromip_ptrrec = ipv6_fromip_host_fqdn + '.'
|
||||||
|
ipv6_fromip_ptr_dnsname = DNSName(ipv6_fromip_ptr)
|
||||||
|
ipv6_fromip_ptr_dn = DN(('idnsname', ipv6_fromip_ptr), revipv6zone_dn)
|
||||||
|
|
||||||
sshpubkey = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGAX3xAeLeaJggwTqMjxNwa6XHBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGIwA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNmcSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM019Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF0L public key test'
|
sshpubkey = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGAX3xAeLeaJggwTqMjxNwa6XHBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGIwA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNmcSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM019Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF0L public key test'
|
||||||
sshpubkeyfp = u'13:67:6B:BF:4E:A2:05:8E:AE:25:8B:A1:31:DE:6F:1B public key test (ssh-rsa)'
|
sshpubkeyfp = u'13:67:6B:BF:4E:A2:05:8E:AE:25:8B:A1:31:DE:6F:1B public key test (ssh-rsa)'
|
||||||
|
|
||||||
@ -684,7 +761,7 @@ class test_host(Declarative):
|
|||||||
desc='Try to add host not in DNS %r without force' % fqdn2,
|
desc='Try to add host not in DNS %r without force' % fqdn2,
|
||||||
command=('host_add', [fqdn2], {}),
|
command=('host_add', [fqdn2], {}),
|
||||||
expected=errors.DNSNotARecordError(
|
expected=errors.DNSNotARecordError(
|
||||||
reason=u'Host does not have corresponding DNS A record'),
|
reason=u'Host does not have corresponding DNS A/AAAA record'),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
@ -958,3 +1035,374 @@ class test_host_false_pwd_change(XMLRPC_test):
|
|||||||
# verify that it's gone
|
# verify that it's gone
|
||||||
with assert_raises(errors.NotFound):
|
with assert_raises(errors.NotFound):
|
||||||
api.Command['host_show'](self.fqdn1)
|
api.Command['host_show'](self.fqdn1)
|
||||||
|
|
||||||
|
|
||||||
|
class test_host_dns(Declarative):
|
||||||
|
|
||||||
|
cleanup_commands = [
|
||||||
|
('host_del', [ipv6only_host_fqdn], {}),
|
||||||
|
('host_del', [ipv4only_host_fqdn], {}),
|
||||||
|
('host_del', [ipv46both_host_fqdn], {}),
|
||||||
|
('host_del', [ipv4_fromip_host_fqdn], {}),
|
||||||
|
('host_del', [ipv6_fromip_host_fqdn], {}),
|
||||||
|
('dnszone_del', [dnszone], {}),
|
||||||
|
('dnszone_del', [revzone], {}),
|
||||||
|
('dnszone_del', [revipv6zone], {}),
|
||||||
|
]
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
dict(
|
||||||
|
desc='Create zone %r' % dnszone,
|
||||||
|
command=(
|
||||||
|
'dnszone_add', [dnszone], {
|
||||||
|
'idnssoamname': dnszone_ns,
|
||||||
|
'idnssoarname': dnszone_rname,
|
||||||
|
'ip_address' : dnszone_ip,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
expected={
|
||||||
|
'value': dnszone_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': dnszone_dn,
|
||||||
|
'idnsname': [dnszone_dnsname],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [dnszone_ns_dnsname],
|
||||||
|
'nsrecord': [dnszone_ns],
|
||||||
|
'idnssoarname': [dnszone_rname_dnsname],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
'idnsallowdynupdate': [u'FALSE'],
|
||||||
|
'idnsupdatepolicy': [u'grant %(realm)s krb5-self * A; '
|
||||||
|
u'grant %(realm)s krb5-self * AAAA; '
|
||||||
|
u'grant %(realm)s krb5-self * SSHFP;'
|
||||||
|
% dict(realm=api.env.realm)],
|
||||||
|
'idnsallowtransfer': [u'none;'],
|
||||||
|
'idnsallowquery': [u'any;'],
|
||||||
|
'objectclass': objectclasses.dnszone,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create reverse zone %r' % revzone,
|
||||||
|
command=(
|
||||||
|
'dnszone_add', [revzone], {
|
||||||
|
'idnssoamname': dnszone_ns,
|
||||||
|
'idnssoarname': dnszone_rname,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
expected={
|
||||||
|
'value': revzone_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': revzone_dn,
|
||||||
|
'idnsname': [revzone_dnsname],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [dnszone_ns_dnsname],
|
||||||
|
'nsrecord': [dnszone_ns],
|
||||||
|
'idnssoarname': [dnszone_rname_dnsname],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
'idnsallowdynupdate': [u'FALSE'],
|
||||||
|
'idnsupdatepolicy': [u'grant %(realm)s krb5-subdomain %(zone)s PTR;'
|
||||||
|
% dict(realm=api.env.realm, zone=revzone)],
|
||||||
|
'idnsallowtransfer': [u'none;'],
|
||||||
|
'idnsallowquery': [u'any;'],
|
||||||
|
'objectclass': objectclasses.dnszone,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create reverse zone %r' % revipv6zone,
|
||||||
|
command=(
|
||||||
|
'dnszone_add', [revipv6zone], {
|
||||||
|
'idnssoamname': dnszone_ns,
|
||||||
|
'idnssoarname': dnszone_rname,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
expected={
|
||||||
|
'value': revipv6zone_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': revipv6zone_dn,
|
||||||
|
'idnsname': [revipv6zone_dnsname],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [dnszone_ns_dnsname],
|
||||||
|
'nsrecord': [dnszone_ns],
|
||||||
|
'idnssoarname': [dnszone_rname_dnsname],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
'idnsallowdynupdate': [u'FALSE'],
|
||||||
|
'idnsupdatepolicy': [u'grant %(realm)s krb5-subdomain %(zone)s PTR;'
|
||||||
|
% dict(realm=api.env.realm, zone=revipv6zone)],
|
||||||
|
'idnsallowtransfer': [u'none;'],
|
||||||
|
'idnsallowquery': [u'any;'],
|
||||||
|
'objectclass': objectclasses.dnszone,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Add A record to %r in zone %r' % (ipv6only, dnszone),
|
||||||
|
command=('dnsrecord_add', [dnszone, ipv6only], {'arecord': arec}),
|
||||||
|
expected={
|
||||||
|
'value': ipv6only_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': ipv6only_dn,
|
||||||
|
'idnsname': [ipv6only_dnsname],
|
||||||
|
'arecord': [arec],
|
||||||
|
'objectclass': objectclasses.dnsrecord,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Add A record to %r in zone %r' % (ipv4only, dnszone),
|
||||||
|
command=('dnsrecord_add', [dnszone, ipv4only], {'aaaarecord': aaaarec}),
|
||||||
|
expected={
|
||||||
|
'value': ipv4only_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': ipv4only_dn,
|
||||||
|
'idnsname': [ipv4only_dnsname],
|
||||||
|
'aaaarecord': [aaaarec],
|
||||||
|
'objectclass': objectclasses.dnsrecord,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Add A record to %r in zone %r' % (ipv46both, dnszone),
|
||||||
|
command=('dnsrecord_add', [dnszone, ipv46both], {'arecord': arec2,
|
||||||
|
'aaaarecord': aaaarec}
|
||||||
|
),
|
||||||
|
expected={
|
||||||
|
'value': ipv46both_dnsname,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': ipv46both_dn,
|
||||||
|
'idnsname': [ipv46both_dnsname],
|
||||||
|
'arecord': [arec2],
|
||||||
|
'aaaarecord': [aaaarec],
|
||||||
|
'objectclass': objectclasses.dnsrecord,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create %r (AAAA record exists)' % ipv6only_host_fqdn,
|
||||||
|
command=('host_add', [ipv6only_host_fqdn],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 5',
|
||||||
|
l=u'Undisclosed location 5',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv6only_host_fqdn,
|
||||||
|
summary=u'Added host "%s"' % ipv6only_host_fqdn,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv6only_host_dn,
|
||||||
|
fqdn=[ipv6only_host_fqdn],
|
||||||
|
description=[u'Test host 5'],
|
||||||
|
l=[u'Undisclosed location 5'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (ipv6only_host_fqdn, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
managedby_host=[ipv6only_host_fqdn],
|
||||||
|
has_keytab=False,
|
||||||
|
has_password=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create %r (A record exists)' % ipv4only_host_fqdn,
|
||||||
|
command=('host_add', [ipv4only_host_fqdn],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 6',
|
||||||
|
l=u'Undisclosed location 6',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv4only_host_fqdn,
|
||||||
|
summary=u'Added host "%s"' % ipv4only_host_fqdn,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv4only_host_dn,
|
||||||
|
fqdn=[ipv4only_host_fqdn],
|
||||||
|
description=[u'Test host 6'],
|
||||||
|
l=[u'Undisclosed location 6'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (ipv4only_host_fqdn, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
managedby_host=[ipv4only_host_fqdn],
|
||||||
|
has_keytab=False,
|
||||||
|
has_password=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create %r (A and AAAA records exist)' % ipv46both_host_fqdn,
|
||||||
|
command=('host_add', [ipv46both_host_fqdn],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 7',
|
||||||
|
l=u'Undisclosed location 7',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv46both_host_fqdn,
|
||||||
|
summary=u'Added host "%s"' % ipv46both_host_fqdn,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv46both_host_dn,
|
||||||
|
fqdn=[ipv46both_host_fqdn],
|
||||||
|
description=[u'Test host 7'],
|
||||||
|
l=[u'Undisclosed location 7'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (ipv46both_host_fqdn, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
managedby_host=[ipv46both_host_fqdn],
|
||||||
|
has_keytab=False,
|
||||||
|
has_password=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create %r with --from-ip option' % ipv4_fromip_host_fqdn,
|
||||||
|
command=('host_add', [ipv4_fromip_host_fqdn],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 8',
|
||||||
|
l=u'Undisclosed location 8',
|
||||||
|
ip_address=ipv4_fromip_ip,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv4_fromip_host_fqdn,
|
||||||
|
summary=u'Added host "%s"' % ipv4_fromip_host_fqdn,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv4_fromip_host_dn,
|
||||||
|
fqdn=[ipv4_fromip_host_fqdn],
|
||||||
|
description=[u'Test host 8'],
|
||||||
|
l=[u'Undisclosed location 8'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (ipv4_fromip_host_fqdn, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
managedby_host=[ipv4_fromip_host_fqdn],
|
||||||
|
has_keytab=False,
|
||||||
|
has_password=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if A record was created for host %r' % ipv4_fromip_host_fqdn,
|
||||||
|
command=('dnsrecord_show', [dnszone, ipv4_fromip], {}
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv4_fromip_dnsname,
|
||||||
|
summary=None,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv4_fromip_dn,
|
||||||
|
idnsname=[ipv4_fromip_dnsname],
|
||||||
|
arecord=[ipv4_fromip_arec],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if PTR record was created for host %r' % ipv4_fromip_host_fqdn,
|
||||||
|
command=('dnsrecord_show', [revzone, ipv4_fromip_ptr], {}
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv4_fromip_ptr_dnsname,
|
||||||
|
summary=None,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv4_fromip_ptr_dn,
|
||||||
|
idnsname=[ipv4_fromip_ptr_dnsname],
|
||||||
|
ptrrecord=[ipv4_fromip_ptrrec],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create %r with --from-ip option (IPv6)' % ipv6_fromip_host_fqdn,
|
||||||
|
command=('host_add', [ipv6_fromip_host_fqdn],
|
||||||
|
dict(
|
||||||
|
description=u'Test host 9',
|
||||||
|
l=u'Undisclosed location 9',
|
||||||
|
ip_address=ipv6_fromip_ipv6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv6_fromip_host_fqdn,
|
||||||
|
summary=u'Added host "%s"' % ipv6_fromip_host_fqdn,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv6_fromip_host_dn,
|
||||||
|
fqdn=[ipv6_fromip_host_fqdn],
|
||||||
|
description=[u'Test host 9'],
|
||||||
|
l=[u'Undisclosed location 9'],
|
||||||
|
krbprincipalname=[u'host/%s@%s' % (ipv6_fromip_host_fqdn, api.env.realm)],
|
||||||
|
objectclass=objectclasses.host,
|
||||||
|
ipauniqueid=[fuzzy_uuid],
|
||||||
|
managedby_host=[ipv6_fromip_host_fqdn],
|
||||||
|
has_keytab=False,
|
||||||
|
has_password=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if AAAA record was created for host %r' % ipv6_fromip_host_fqdn,
|
||||||
|
command=('dnsrecord_show', [dnszone, ipv6_fromip], {}
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv6_fromip_dnsname,
|
||||||
|
summary=None,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv6_fromip_dn,
|
||||||
|
idnsname=[ipv6_fromip_dnsname],
|
||||||
|
aaaarecord=[ipv6_fromip_aaaarec],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if PTR record was created for host %r' % ipv6_fromip_host_fqdn,
|
||||||
|
command=('dnsrecord_show', [revipv6zone, ipv6_fromip_ptr], {}
|
||||||
|
),
|
||||||
|
expected=dict(
|
||||||
|
value=ipv6_fromip_ptr_dnsname,
|
||||||
|
summary=None,
|
||||||
|
result=dict(
|
||||||
|
dn=ipv6_fromip_ptr_dn,
|
||||||
|
idnsname=[ipv6_fromip_ptr_dnsname],
|
||||||
|
ptrrecord=[ipv6_fromip_ptrrec],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
@ -2851,9 +2851,9 @@ class test_permission_legacy(Declarative):
|
|||||||
command=('permission_find', [],
|
command=('permission_find', [],
|
||||||
{'ipapermlocation': api.env.basedn}),
|
{'ipapermlocation': api.env.basedn}),
|
||||||
expected=dict(
|
expected=dict(
|
||||||
count=15,
|
count=16,
|
||||||
truncated=False,
|
truncated=False,
|
||||||
summary=u'15 permissions matched',
|
summary=u'16 permissions matched',
|
||||||
result=lambda s: True,
|
result=lambda s: True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -71,7 +71,17 @@ class test_realmdomains(Declarative):
|
|||||||
u'(version 3.0;acl '
|
u'(version 3.0;acl '
|
||||||
u'"permission:System: Read Realm Domains";'
|
u'"permission:System: Read Realm Domains";'
|
||||||
u'allow (compare,read,search) '
|
u'allow (compare,read,search) '
|
||||||
u'userdn = "ldap:///all";)'
|
u'userdn = "ldap:///all";)',
|
||||||
|
|
||||||
|
u'(targetattr = "associateddomain")'
|
||||||
|
u'(targetfilter = "(objectclass=domainrelatedobject)")'
|
||||||
|
u'(version 3.0;acl '
|
||||||
|
u'"permission:System: Modify Realm Domains";'
|
||||||
|
u'allow (write) groupdn = "ldap:///%s";)' %
|
||||||
|
DN('cn=System: Modify Realm Domains',
|
||||||
|
api.env.container_permission,
|
||||||
|
api.env.basedn),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user