Автор Тема: Перенос базы на новый суффикс  (Прочитано 8894 раз)

marawu

  • Пользователь
  • **
  • Сообщений: 76
  • !
    • Просмотр профиля
Перенос базы на новый суффикс
« : 29 Сентябрь 2017, 14:32:14 »
Здраствуйте. Есть задача поменять суффикс в базе openLDAP. Хотел посоветоваться. Я решил старый суффикс не убивать, мало ли пригодится. Какой план действий я вижу:


Есть база:



dn: olcDatabase={1}bdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcBdbConfig
olcDatabase: {1}bdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example,dc=com
olcAccess: {0}to attrs=userPassword by self write by group.exact="cn=Directory Administrators,dc=example,dc=com" write by dn="uid=ldap-proxy,ou=Special Users,dc=example,dc=com" read by dn="cn=outldap02,dc=example,dc=com" read by dn="cn=replica,dc=example,dc=com" read by anonymous auth by * none
olcAccess: {1}to filter="(sshPublicKey=*)" attrs=entry,uid,sshPublicKey by anonymous read by * break
olcAccess: {2}to attrs=sshPublicKey by self write by group.exact="cn=Directory Administrators,dc=example,dc=com" write by dn.one="ou=Special Users,dc=example,dc=com" read by * none
olcAccess: {3}to dn.subtree="ou=Special Users,dc=example,dc=com" by self write by group.exact="cn=Directory Administrators,dc=example,dc=com" write by dn.one="ou=Special Users,dc=example,dc=com" read by dn="cn=outldap02,dc=example,dc=com" read by dn="cn=replica,dc=example,dc=com" read by * none
olcAccess: {4}to dn.one="cn=config,dc=example,dc=com" by * read
olcAccess: {5}to * by peername.ip="xx.xx.xx.xx" read by peername.ip="xx.xx.xx.xx" read by peername.ip="192.168.250.78" read by self write by group.exact="cn=Directory Administrators,dc=example,dc=com" write by users read by * search
olcLastMod: TRUE
olcLimits: {0}dn.exact="cn=replica,dc=example,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
olcRootDN: cn=admin,dc=example,dc=com
olcRootPW: {SSHA}HlnqlaPuqKyfvtpISYz/xvBbhDpQu2NX
olcSizeLimit: -1
olcDbCheckpoint: 512 30
olcDbConfig: {0}set_cachesize 0 2097152 0
olcDbConfig: {1}set_lk_max_objects 1500
olcDbConfig: {2}set_lk_max_locks 1500
olcDbConfig: {3}set_lk_max_lockers 1500
olcDbIndex: uid eq
olcDbIndex: uidNumber eq
olcDbIndex: uniqueMember eq
olcDbIndex: gidNumber eq
olcDbIndex: memberUid eq
olcDbIndex: alias eq
olcDbIndex: mail eq
olcDbIndex: sambaSID eq
olcDbIndex: sambaPrimaryGroupSID eq
olcDbIndex: sambaGroupType eq
olcDbIndex: sambaDomainName eq
olcDbIndex: loginShell eq
olcDbIndex: default sub
olcDbIndex: objectClass,entryCSN,entryUUID eq
olcDbIndex: ou eq
olcDbIndex: cn eq
olcDbIndex: apple-generateduid eq
olcDbIndex: apple-group-memberguid eq


Я так понимаю мне необходимо создать LDIF с такими же параметрами, но заменить суффик. Должно получиться типа такого:



dn: olcDatabase={3}bdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcBdbConfig
olcDatabase: {3}bdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example1,dc=com
olcAccess: {0}to attrs=userPassword by self write by group.exact="cn=Directory Administrators,dc=example1,dc=com" write by dn="uid=ldap-proxy,ou=Special Users,dc=example1,dc=com" read by dn="cn=outldap02,dc=example1,dc=com" read by dn="cn=replica,dc=example1,dc=com" read by anonymous auth by * none
olcAccess: {1}to filter="(sshPublicKey=*)" attrs=entry,uid,sshPublicKey by anonymous read by * break
olcAccess: {2}to attrs=sshPublicKey by self write by group.exact="cn=Directory Administrators,dc=example1,dc=com" write by dn.one="ou=Special Users,dc=example1,dc=com" read by * none
olcAccess: {3}to dn.subtree="ou=Special Users,dc=example1,dc=com" by self write by group.exact="cn=Directory Administrators,dc=example1,dc=com" write by dn.one="ou=Special Users,dc=example1,dc=com" read by dn="cn=outldap02,dc=example1,dc=com" read by dn="cn=replica,dc=example1,dc=com" read by * none
olcAccess: {4}to dn.one="cn=config,dc=example1,dc=com" by * read
olcAccess: {5}to * by peername.ip="xx.xx.xx.xx" read by peername.ip="xx.xx.xx.xx" read by peername.ip="192.168.250.78" read by self write by group.exact="cn=Directory Administrators,dc=example1,dc=com" write by users read by * search
olcLastMod: TRUE
olcLimits: {0}dn.exact="cn=replica,dc=example1,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
olcRootDN: cn=admin,dc=example1,dc=com
olcRootPW: {SSHA}HlnqlaPuqKyfvtpISYz/xvBbhDpQu2NX
olcSizeLimit: -1
olcDbCheckpoint: 512 30
olcDbConfig: {0}set_cachesize 0 2097152 0
olcDbConfig: {1}set_lk_max_objects 1500
olcDbConfig: {2}set_lk_max_locks 1500
olcDbConfig: {3}set_lk_max_lockers 1500
olcDbIndex: uid eq
olcDbIndex: uidNumber eq
olcDbIndex: uniqueMember eq
olcDbIndex: gidNumber eq
olcDbIndex: memberUid eq
olcDbIndex: alias eq
olcDbIndex: mail eq
olcDbIndex: sambaSID eq
olcDbIndex: sambaPrimaryGroupSID eq
olcDbIndex: sambaGroupType eq
olcDbIndex: sambaDomainName eq
olcDbIndex: loginShell eq
olcDbIndex: default sub
olcDbIndex: objectClass,entryCSN,entryUUID eq
olcDbIndex: ou eq
olcDbIndex: cn eq
olcDbIndex: apple-generateduid eq
olcDbIndex: apple-group-memberguid eq


Затем сделать выгрузку содержимого каталога с переименованным суффиксом в ldif файл, что-то типа такого:


ldapsearch  -o ldif-wrap=no -QLLL -Y EXTERNAL -H ldapi:/// -b dc=example,dc=com | sed 's/example/example1/g'  > base.ldif


и вгрузить его в новую базу.


Насколько корректен такой перенос?


ЗЫ. Понятное дело, что ещё сертификаты надо будет поменять.

egor

  • Администратор
  • Старожил
  • *****
  • Сообщений: 486
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #1 : 30 Сентябрь 2017, 01:01:00 »
Здравствуйте! Всё, вроде нормально, единственное, я бы использовал slapcat вместо ldapsearch для выгрузки. Похожий вопрос уже обсуждался почти 5 лет назад.

Егор

marawu

  • Пользователь
  • **
  • Сообщений: 76
  • !
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #2 : 04 Октябрь 2017, 11:43:11 »
Здравствуйте! Всё, вроде нормально, единственное, я бы использовал slapcat вместо ldapsearch для выгрузки. Похожий вопрос уже обсуждался почти 5 лет назад.

Егор

В принципе я почти со всем разобрался, но осталось одна проблема. Когда я делаю выгрузку

slapcat -o ldif-wrap=no -b dc=example,dc=com -l /tmp/dump.ldif

то у меня пароли выгружаются в некорректном виде: userPassword:: e1NTSEF9Q3Z5d3EwSFphcGMrYUVqUXFiTHVBRmFYbnlnMmRrdHVORzVrUVE9PQ== (а так же поля, где присутствует кириллица)

Если я например сделаю так:
ldapsearch -LLL -xZZWD "uid=user1,ou=People,dc=example,dc=com" -b dc=example,dc=com uid=user2 | perl -MMIME::Base64 -MEncode=decode -n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode("UTF-8",decode_base64($1))/eg;print'

то они выгружаются в нормальном виде: userPassword:: {SSHA}lY2HtDIK/vQ/f58gaqa4zv9vwAhMWWlRKE5AYQ==

Но я не могу их вгрузить в новую базу:

ldif_parse_line: givenName: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: userPassword: invalid base64 encoding char ({) 0x7b
ldif_parse_line: sn: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: displayName: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: cn: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: initials: invalid base64 encoding char ( 0xffffffd1
ldif_parse_line: givenName: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: userPassword: invalid base64 encoding char ({) 0x7b
ldif_parse_line: sn: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: displayName: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: cn: invalid base64 encoding char ( 0xffffffd0
ldif_parse_line: initials: invalid base64 encoding char ( 0xffffffd1
ldif_parse_line: givenName: invalid base64 encoding char ( 0xffffffd0

Как можно перенести базу с сохранением паролей?

marawu

  • Пользователь
  • **
  • Сообщений: 76
  • !
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #3 : 04 Октябрь 2017, 12:29:11 »
В общем победил, пришлось в ручную ldif править (убрать лишнее двоеточие и переносы строк не везде корректные), некоторые поля он не смог принять, например типа поля description с таким содержимым:



description:: <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Attribute Type Map</key>
<array/>
<key>Record Type Map</key>
<array>
<dict>
<key>Attribute Type Map</key>
<array>
<dict>
<key>Native Map</key>
<array>
<string>modifyTimestamp</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:ModificationTimestamp</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>shadowExpire</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Expire</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>createTimestamp</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:CreationTimestamp</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>shadowLastChange</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Change</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>givenName</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:FirstName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>loginShell</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:UserShell</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-generateduid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:GeneratedUID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>gidNumber</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:PrimaryGroupID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>uid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:RecordName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>mail</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:EMailAddress</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>sn</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:LastName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>description</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Comment</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>userPassword</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Password</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>cn</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:RealName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>uidNumber</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:UniqueID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-user-homeurl</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:NFSHomeDirectory</string>
</dict>
</array>
<key>Native Map</key>
<array>
<dict>
<key>Group Object Classes</key>
<string>AND</string>
<key>Object Classes</key>
<array>
<string>inetOrgPerson</string>
<string>posixAccount</string>
<string>shadowAccount</string>
<string>apple-user</string>
<string>extensibleObject</string>
</array>
<key>Search Base</key>
<string>ou=People, %!</string>
</dict>
</array>
<key>Standard Name</key>
<string>dsRecTypeStandard:Users</string>
</dict>
<dict>
<key>Attribute Type Map</key>
<array>
<dict>
<key>Native Map</key>
<array>
<string>apple-generateduid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:GeneratedUID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>ttl</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:TimeToLive</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-realname</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:RealName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>memberUid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:GroupMembership</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-user-picture</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Picture</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>sambaSID</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:SMBSID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>description</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Comment</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-nestedgroup</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:NestedGroups</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>labeledURI</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:URL</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-mcxflags</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:MCXFlags</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-keyword</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Keywords</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>gidNumber</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:PrimaryGroupID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>primaryGroupID</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:SMBGroupRID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-homeowner</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:HomeLocOwner</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-xmlplist</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:XMLPlist</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-services</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:GroupServices</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-mcxsettings</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:MCXSettings</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-contactguid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:ContactGUID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>jpegPhoto</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:JPEGPhoto</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>modifyTimestamp</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:ModificationTimestamp</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-profiles-timestamp</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:ProfilesTimestamp</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-profiles</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Profiles</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>cn</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:RecordName</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>rid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:SMBRID</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-serviceslocator</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:ServicesLocator</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>memberUid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:Member</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-homeurl</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:HomeDirectory</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-group-memberguid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:GroupMembers</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>createTimestamp</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:CreationTimestamp</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>mail</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:EMailAddress</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-mapcoordinates</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:MapCoordinates</string>
</dict>
<dict>
<key>Native Map</key>
<array>
<string>apple-ownerguid</string>
</array>
<key>Standard Name</key>
<string>dsAttrTypeStandard:OwnerGUID</string>
</dict>
</array>
<key>Native Map</key>
<array>
<dict>
<key>Group Object Classes</key>
<string>AND</string>
<key>Object Classes</key>
<array>
<string>posixGroup</string>
<string>apple-group</string>
<string>extensibleObject</string>
</array>
<key>Search Base</key>
<string>ou=Groups, %!</string>
</dict>
</array>
<key>Standard Name</key>
<string>dsRecTypeStandard:Groups</string>
</dict>
</array>
</dict>
</plist>


А это нужно для автоматической настройки mac OS. Придется видимо вручную заливать по новой

egor

  • Администратор
  • Старожил
  • *****
  • Сообщений: 486
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #4 : 04 Октябрь 2017, 13:12:53 »
у меня пароли выгружаются в некорректном виде: userPassword:: e1NTSEF9Q3Z5d3EwSFphcGMrYUVqUXFiTHVBRmFYbnlnMmRrdHVORzVrUVE9PQ== (а так же поля, где присутствует кириллица)

Это, на самом деле, нормальный вид, просто значение атрибута содержит символы, которые не входят в набор "безопасных" (по мнению RFC 2849), и, согласно тому же RFC, это значение кодируется в base64, а после имени атрибута ставится два двоеточия. Так что такие значения прекрасно выгружаются и загружаются обратно в каталог без всяких преобразований. В том числе и то длинное, которое у Вас в атрибуте description.

Егор

marawu

  • Пользователь
  • **
  • Сообщений: 76
  • !
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #5 : 04 Октябрь 2017, 14:06:44 »
Да это я понимаю, но пароли у меня перестали подходить

egor

  • Администратор
  • Старожил
  • *****
  • Сообщений: 486
    • Просмотр профиля
Re: Перенос базы на новый суффикс
« Ответ #6 : 07 Октябрь 2017, 01:14:07 »
Да это я понимаю, но пароли у меня перестали подходить

Бэкапил (slapcat) и восстанавливал (slapcat) базы неоднократно, с кириллицей, всегда работало нормально.

Только что ещё раз специально попробовал =) . В бэкапе пароль и кириллица закодированы в base64:
$ ldapsearch -x -LLL -H ldap://127.1:9000 -D uid=egor,dc=mycompany,dc=ru -w firstPassword -b dc=mycompany,dc=ru dn
dn: dc=mycompany,dc=ru

dn: uid=egor,dc=mycompany,dc=ru

$ slapcat -f ./slapd.conf -n1 -l ./backup.ldif
$ cat ./backup.ldif
dn: dc=mycompany,dc=ru
objectClass: organization
objectClass: dcObject
dc: mycompany
o: My Company
structuralObjectClass: organization
entryUUID: 3351bed6-3f2e-1037-94fb-f7cca8266f00
creatorsName: cn=manager,dc=mycompany,dc=ru
createTimestamp: 20171006220525Z
entryCSN: 20171006220525.612677Z#000000#000#000000
modifiersName: cn=manager,dc=mycompany,dc=ru
modifyTimestamp: 20171006220525Z

dn: uid=egor,dc=mycompany,dc=ru
objectClass: inetOrgPerson
uid: egor
cn:: 0JXQs9C+0YAg0JvQtdCy0LjQvdGG0LA=
sn:: 0JvQtdCy0LjQvdGG0LA=
userPassword:: e1NTSEF9K2Yzc01kRUdRMU5rNzE5dHRMWEZnQm5wZVVNUWlKQjE=
structuralObjectClass: inetOrgPerson
entryUUID: 336155e4-3f2e-1037-94fc-f7cca8266f00
creatorsName: cn=manager,dc=mycompany,dc=ru
createTimestamp: 20171006220525Z
entryCSN: 20171006220525.714853Z#000000#000#000000
modifiersName: cn=manager,dc=mycompany,dc=ru
modifyTimestamp: 20171006220525Z

$ slapadd -f ./slapd.conf -n 1 -l ./backup.ldif
_#################### 100.00% eta   none elapsed            none fast!         
Closing DB...
$ ldapsearch -x -LLL -H ldap://127.1:9000 -D uid=egor,dc=mycompany,dc=ru -w firstPassword -b dc=mycompany,dc=ru dn
dn: dc=mycompany,dc=ru

dn: uid=egor,dc=mycompany,dc=ru

После восстановления пароль работает прекрасно.

Егор
« Последнее редактирование: 07 Октябрь 2017, 01:21:58 от egor »