Introduction
Comment un user peut-il interagir avec les objets d'un autre user? Est-ce facile? Impossible? Les problématiques de droits et de rôles sont complexes avec Oracle mais il existe différentes techniques permettant à un user U1 d'accéder aux objets d'un user U2 et c'est ce que nous allons voir.


 
Points d'attention
Trop pour les énumérer :-)



Base de tests
N'importe quelle base Oracle.



Exemples

Dans la suite de cet article, j'utiliserais les conventions suivantes :
- U1 : user zztest, avec le droit CREATE SESSION au minimum.
- Au début de chaque test, on créé une table zz01 appelée ci-après T1 ou un dblink.
- U2 : user zztest02, avec le droit CREATE SESSION au minimum voir DBA.
- Entre chaque test on supprime les users pour éviter que des rôles ou droits ne traînent et ne faussent les tests suivants.
      
Le test a réaliser pour vérifier si on interagit avec un autre schéma est simple : U1 doit supprimer ou interroger un objet du schéma U2.
      
============================================================================================
Solution 1 : user avec le rôle DBA ou bien user SYS
============================================================================================

Dans ce test, U1 a le rôle DBA ou bien il est SYS : il peut quasiment tout faire, y compris dropper des objets du schéma U2 (sauf les dblinks mais c'est une autre histoire).
     SQL> show user
     USER est "SYS"
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
      
     SQL> connect zztest02
     SQL> create table zz01 (id number);
      
     SQL> connect / as sysdba
      
Le test est OK : le user SYS peut intéragir avec les objets du schéma zztest02 par défaut; ici, il drop une table.
     SQL> drop table zztest02.zz01;
     Table supprimee.
      
     SQL> drop user zztest02 cascade;
     
      
============================================================================================
Solution 2 : user qui a reçu des droits sur les objets d'un autre user
============================================================================================
    
Cette fois U1 n'a pas le rôle DBA mais il a les droits sur les objets de U2, donnés justement par U2.
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant resource, create session to zztest02;
     SQL> connect zztest02
     SQL> create table zz01 (id number);
      
     SQL> connect / as sysdba
     SQL> show user
     USER est "SYS"
      
     SQL> connect zztest
     SQL> select * from zztest02.zz01;
     select * from zztest02.zz01
                            *
     ERREUR a la ligne 1 :
     ORA-00942: Table ou vue inexistante
      
     SQL> connect zztest02
     SQL> grant select on zz01 to zztest;
      
Cette fois c'est OK, U1 accède bien à la table de U2.
     SQL> connect zztest
     SQL> select * from zztest02.zz01;
     aucune ligne selectionnee
      
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;

     
============================================================================================
Solution 3 : user qui connait le password d'un autre user
============================================================================================
    
U1 cette fois connaît le mot de passe de U2 : soit on le lui a dit, soit il a été récupéré dans un fichier excel (ne riez pas, c'est courant), soit deviné... Quoi qu'il en soit, U1 devient à un instant t le user U2.
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> create table zz01 (id number);
      
Maintenant le user U1 se connecte puis il va se connecter comme U2 pour accéder à ses objets .
     SQL> connect zztest
     SQL> connect zztest02

U1 saisit le mot de passe de U2 et drop une de ses tables.
     SQL> drop table zz01;
     Table supprimee.
      
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
     

============================================================================================
Solution 4 : avoir le droit ANY
============================================================================================
    
Dans la longue liste des privilèges système, il en est une catégorie particulière, celle qui comprends le mot ANY. Elle permet à un utilisateur d'agir sur un autre schéma sans connaître son password... puissant, non?
     SQL> select name from SYSTEM_PRIVILEGE_MAP where name like '%ANY%' order by 1;
     NAME
     ------------
     ...
     ALTER ANY INDEX
     ALTER ANY PROCEDURE
     ALTER ANY ROLE
     ...
     CREATE ANY TABLE
     CREATE ANY TRIGGER
     ...

     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> grant create any table to zztest;

     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> create table zz01 (id number);


Et maintenant, le user U1 va créer une table dans le schéma du user U2; cette table sera en revanche propriété du user U2 et non pas de son créateur.
     SQL> connect zztest
     SQL> create table zztest02.test_any (id date);
     SQL> connect SYS@ORCL as sysdba
     SQL> select owner from dba_tables where table_name = 'TEST_ANY';
     OWNER
     ---------------
     ZZTEST02

Et on voit même que le créateur de la table ne peut pas faire de SELECT dessus; vous notez qu'on a pas le message "Table inexistante", ce qui prouve que avec le droit CREATE ANY, le user connaît la table qu'il a créé mais il lui faut le droit SELECT dessus pour l'interroger.
     SQL> connect zztest
     SQL> select * from zztest02.test_any;
     select * from zztest02.test_any
                            *
     ERROR at line 1:
     ORA-01031: insufficient privileges


============================================================================================
Solution 5 : changer de schéma courant avec CURRENT_SCHEMA
============================================================================================
    
Avec le paramètre CURRENT_SCHEMA, un user peut se connecter à un autre schéma mais, attention, pas à un autre compte. Il reste lui même mais Oracle va chercher les objets non préfixés par un nom de schéma dans le schéma identifié par CURRENT_SCHEMA.
     SQL> show user
     USER est "SYS" :
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> grant SELECT ANY DICTIONARY to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> create table zz01 (id number);
     SQL> connect zztest;
     SQL> show user
     USER est "ZZTEST"
      
     SQL> ALTER SESSION SET CURRENT_SCHEMA = "ZZTEST02";
     Session modifiee.
      
Voilà, on reste le user U1 mais on accède directement au schéma U2 sans avoir à préfixer les objets par le nom du propriétaire.
     SQL> select username, schemaname from v$session where sid in (select sid from v$mystat);
     USERNAME                       SCHEMANAME
     ------------------------------ ------------------------------
     ZZTEST                         ZZTEST02
      
     SQL> select * from zz01;
     select * from zz01
                   *
     ERREUR a la ligne 1 :
     ORA-00942: Table ou vue inexistante
      
OK, il faut ajouter pour le premier user le droit SELECT sur la table du user U2 :-)
     SQL> connect zztest02
     SQL> grant select on zz01 to zztest;
     SQL> connect zztest
     SQL> ALTER SESSION SET CURRENT_SCHEMA = "ZZTEST02";
      
     SQL> select username, schemaname from v$session where sid in (select sid from v$mystat);
     USERNAME                       SCHEMANAME
     ------------------------------ ------------------------------
     ZZTEST                         ZZTEST02
      
Et voilà, en tant que U1 j'ai accès à un objet du schéma U2 sans avoir à préfixer celui-ci par le nom du propriétaire.
     SQL> select * from zz01;
     aucune ligne selectionnee
      
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
      
      
============================================================================================
Solution 6 : lancer une procédure créée dans un autre schéma
============================================================================================

Plus complexe, comment supprimer le dblink d'un autre schéma? Même SYS ne le peut pas car il est impossible de préfixer le nom d'un dblink par le nom d'un schéma, comme le dit la doc Oracle et aucun user ne peut supprimer le dblink d'un autre schéma.
https://docs.oracle.com/database/121/SQLRF/statements_8011.htm#SQLRF01514
"Restriction on Dropping Database Links
You cannot drop a database link in another user's schema, and you cannot qualify dblink with the name of a schema, because periods are permitted in names of database links. Therefore, Oracle Database interprets the entire name, such as ralph.linktosales, as the name of a database link in your schema rather than as a database link named linktosales in the schema ralph."
      
La solution est que U1 crée une procédure dans le schéma U2 et exécute celle-ci : le drop dblink est alors possible puisque la procédure s'exécutera dans le schéma U2!
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> CREATE DATABASE LINK DBLINK01 USING '10.142.111.888:1521/DLAWWW';
      
     SQL> connect sys as sysdba
     SQL> show user
     USER est "SYS"
      
     SQL> select OWNER, DB_LINK from dba_db_links where db_link like '%01';
     OWNER           DB_LINK
     ---------------------------
     ZZTEST02        DBLINK01

SYS lui même ne peut pas dropper un dblink d'un autre user car s'il le préfixe par le nom du propriétaire, Oracle considère que le point (.) fait partie du nom de l'objet et le cherche dans le schéma courant, donc celui de SYS... où il n'existe pas!
     SQL> drop database link zztest02.DBLINK01;
     drop database link zztest02.DBLINK01
                        *
     ERREUR a la ligne 1 :
     ORA-02024: lien de base de donnees introuvable

Plus vicieux : si on change le schéma courant du user SYS avec comme objectif de ne pas préfixer le dblink du nom du user, on arrive à toucher celui-ci MAIS on a un autre message d'erreur, disant "Privilèges insuffisants" : personne, pas même SYS, ne peut supprimer le db link d'un autre utilisateur.
     SQL> show user
     USER is "SYS"

     SQL> alter session set current_schema = "ZZTEST02";
     Session altered.

     SQL> drop database link DBLINK01;
     drop database link DBLINK01
     *
     ERROR at line 1:
     ORA-01031: insufficient privileges

On crée une procédure dans le schéma zztest02. Comme on appelle cette procédure depuis un autre schéma, on est donc virtuellement, lors de l'exécution de cette procédure, l'autre user!
     CREATE OR REPLACE PROCEDURE ZZTEST02.PROC_DROP_DBLINK IS
     BEGIN
         execute immediate 'drop database link dblink01';
         EXCEPTION
          WHEN OTHERS THEN
            dbms_output.put_line('PB : SQLCODE ' || to_char(SQLCODE) || ': ' || SQLERRM);
         END PROC_DROP_DBLINK;
     /
      
Attention, le message "Procedure PL/SQL terminee avec succes." ne signifie pas qu'il n'y a pas eu d'erreur mais juste que la procédure s'est exécutée.
     SQL> execute ZZTEST02.PROC_DROP_DBLINK;
     Procedure PL/SQL terminee avec succes.
      
C'est bon!! Plus de dblink dans le schéma U2 :-) On a bien exécuté du code comme étant ce user U2 mais sans connaître son password.
     SQL> select OWNER, DB_LINK from dba_db_links where db_link like '%01';
     aucune ligne selectionnee
      
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
      
      
============================================================================================
Solution 7 : package non documenté DBMS_SYS_SQL
============================================================================================

Avec le package PL/SQL de nom DBMS_SYS_SQL, un user peut exécuter du code en étant un autre user, avec tous ses droits. ATTENTION, ce package est non documenté par Oracle, ne l'utilisez pas en production, on ne connaît pas les effets secondaires :-)
Ce package permet de faire du SQL dynamique, comme DBMS_SQL, mais en ajoutant une subtilité qui est de dire quel user va réellement exécuter le code. Vous avez compris? Un user Stagiaire peut devenir SYS avec ce package! Attention, comme pré-requis il faut avoir le droit EXECUTE dessus et SELECT sur DBA_USERS.
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> grant select on dba_users to zztest;
     SQL> grant execute on dbms_sys_sql to zztest;
      
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> create table zz01 (id number);
      
     SQL> connect zztest
     SQL> select * from zztest02.zz01;
     select * from zztest02.zz01
                            *
     ERREUR a la ligne 1 :
     ORA-00942: Table ou vue inexistante
      
     SQL> connect sys as sysdba
     SQL> show user
     USER est "SYS"
      
     SQL> desc zztest02.zz01;
     Nom                                       NULL ?   Type
     ----------------------------------------- -------- ----------------------------
     ID                                                 NUMBER
      
     SQL> connect zztest
      
     SQL> desc zztest02.zz01
     ERROR:
     ORA-04043: objet zztest02.zz01 inexistant
      
     SQL> SET SERVEROUTPUT ON
            declare
                    v_n_uid number;
                    v_v_sqltext varchar2(100) := 'ALTER TABLE zztest02.zz01 ADD TESTDATE DATE';
                    v_i_myint integer;
       
            begin
                    SELECT USER_ID INTO V_N_UID FROM DBA_USERS WHERE USERNAME = 'SYS';
                    v_i_myint:=sys.dbms_sys_sql.open_cursor()
                   
sys.dbms_sys_sql.parse_as_user(v_i_myint,v_v_sqltext,dbms_sql.native,v_n_uid);
                    sys.dbms_sys_sql.close_cursor(v_i_myint);
     
            exception
                    when others then
                          dbms_output.put_line('failed to execute the specified statement for user: '|| to_char(v_n_uid));
                          dbms_output.put_line(substr(sqlerrm, 1, 100));
       end ;
     /
      
     Procedure PL/SQL terminee avec succes.
      
Pour voir si c'est OK, il faut se connecter comme zztest02 et faire un desc de la table.
Bingo, le test a réussi : U1 a bien modifié un objet appartenant à U2 sans en avoir les droits!
     SQL> connect zztest02
     SQL> desc zz01
     Nom                                       NULL ?   Type
     ----------------------------------------- -------- ----------------------------
     ID                                                 NUMBER
     TESTDATE                                           DATE
      
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
      
     
============================================================================================
Solution 8 : les proxy users
============================================================================================

La fonctionnalité Proxy User de Oracle permet de se connecter comme un autre user sans connaître son password! Regardons cela de plus près.
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest02 identified by TotoTiti31380000;
     SQL> grant dba to zztest02;
     SQL> create user zztest identified by YukioYukio31380000;
     SQL> grant create session to zztest;
      
La commande magique est ici : le user zztest pourra maintenant se connecter comme zztest02.
     SQL> alter user zztest02 grant connect through zztest ;
     
     SQL> connect zztest02
     SQL> CREATE DATABASE LINK DBLINK01 USING '10.142.111.888:1521/DLAWWW';
     SQL> select OWNER, DB_LINK from dba_db_links where db_link like '%01';
     OWNER         DB_LINK
     ----------------------
     ZZTEST02      DBLINK01
      
On se connecte au compte zztest02, sans connaître son password, à partir du compte zztest.
Attention à la syntaxe : compte1[compte2]/password compte 1
     SQL> connect zztest[zztest02]/YukioYukio31380000;
     SQL> show user
     USER est "ZZTEST02"
     SQL> drop database link dblink01;
     SQL> select OWNER, DB_LINK from dba_db_links where db_link like '%01';
     aucune ligne selectionnee
      
     SQL> connect / as sysdba
     SQL> alter user zztest02 revoke connect through zztest ;
     
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
     

============================================================================================
Solution 9 : ALTER USER IDENTIFIED BY VALUES - changer le mot de passe temporairement d'un user
============================================================================================

Un user se connecte comme un autre user en changeant « temporairement » son mot de passe car il ne connaît pas son password.
L’ordre SQL est ALTER USER user IDENTIFIED BY VALUES "champ spare4 ou champ password" ;
Je teste sur une autre base, ne soyez pas surpris :-)
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> CREATE DATABASE LINK DBLINK01 USING '127.0.0.1/ORCL';
      
     SQL> connect SYS/oracle@ORCL as sysdba
     SQL> show user
     USER est "SYS"
    
     SQL> desc user$
      Name                       Null?    Type
      ----------------------------------------- ---------
      USER#                       NOT NULL NUMBER
      NAME                       NOT NULL VARCHAR2(128)
      TYPE#                       NOT NULL NUMBER
      PASSWORD                        VARCHAR2(4000)
      DATATS#                   NOT NULL NUMBER
      TEMPTS#                   NOT NULL NUMBER
      CTIME                       NOT NULL DATE
      PTIME                            DATE
      EXPTIME                        DATE
      LTIME                            DATE
      RESOURCE$                   NOT NULL NUMBER
      AUDIT$                         VARCHAR2(38)
      DEFROLE                   NOT NULL NUMBER
      DEFGRP#                        NUMBER
      DEFGRP_SEQ#                        NUMBER
      ASTATUS                   NOT NULL NUMBER
      LCOUNT                    NOT NULL NUMBER
      DEFSCHCLASS                        VARCHAR2(128)
      EXT_USERNAME                        VARCHAR2(4000)
      SPARE1                         NUMBER
      SPARE2                         NUMBER
      SPARE3                         NUMBER
      SPARE4                         VARCHAR2(1000)
      SPARE5                         VARCHAR2(1000)
      SPARE6                         DATE
      SPARE7                         VARCHAR2(4000)
      SPARE8                         VARCHAR2(4000)
      SPARE9                         NUMBER
      SPARE10                        NUMBER
      SPARE11                        TIMESTAMP(6)

Avant de changer le mot de passe du user, il faut avoir bien pris soin de récupérer son mot de passe crypté avant.      
     SQL> SELECT nvl(password, 'RIEN'), spare4 FROM user$ WHERE name = 'ZZTEST02';
     NVL(PASSWORD,'RIEN')    SPARE4
     ------------------------------------------
     RIEN                  S:B35FA9749EDA9A6C3CF3A198D2B8BD0F1DCB10101CEEFC48801700F8AFA0;T:B72307284B19A51A5E4BA7596DC3BDBCC99CF89D12FD23E662343EAA26A93B226DD7E60062890EE61E89D1032176311859FB0954CD781C1AC2536ECDBC25E12874F5EC6345D3E69C843F08FB63852026
     
On saisit un nouveau mot de passe pour U2 et on se contacte à son compte pour dropper le dblink.
     SQL> alter user ZZTEST02 identified by toto;
     SQL> connect zztest02/toto@orcl;
     SQL> drop database link dblink01;
     SQL> connect SYS/oracle@ORCL as sysdba

Et maintenant on remet l'ancien password du user en utilisant la valeur cryptée.    
     SQL> alter user zztest02 identified by values 'S:B35FA9749EDA9A6C3CF3A198D2B8BD0F1DCB10101CEEFC48801700F8AFA0;T:B72307284B19A51A5E4BA7596DC3BDBCC99CF89D12FD23E662343EAA26A93B226DD7E60062890EE61E89D1032176311859FB0954CD781C1AC2536ECDBC25E12874F5EC6345D3E69C843F08FB63852026';
     User altered.
    
C'est bon, l'ancien mot de passe est actif.
     SQL> connect zztest02/toto@ORCL;
     ERROR:
     ORA-01017: invalid username/password; logon denied
     Warning: You are no longer connected to ORACLE.
     
     SQL> connect zztest02/YukioYukio31380000@ORCL;
     Connected.
     SQL> show user
     USER is "ZZTEST02"
     SQL> select OWNER, DB_LINK from dba_db_links where db_link like '%01';
     no rows selected
     
     SQL> drop user zztest;
     SQL> drop user zztest02 cascade;
     
    
============================================================================================
Solution 10 : fonction système KUPP$PROC
============================================================================================

La fonction KUPP$PROC permet de  changer de user; cette astuce fonctionnait en 11 mais visiblement pas en 12.1.
https://vigilance.fr/vulnerabilite/Oracle-DB-elevation-de-privileges-via-BECOME-USER-7339
Les rôles DBA et IMP_FULL_DATABASE possèdent le privilège "BECOME USER" qui permet de changer d'identité. Ce changement d'identité se fait via sys.kupp$proc.change_user().
     SQL> show user
     USER est "SYS"
      
     SQL> create user zztest identified by TotoTiti31380000 ;
     SQL> grant create session to zztest;
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;
     SQL> connect zztest02
     SQL> CREATE DATABASE LINK DBLINK01 USING '127.0.0.1/ORCL';
      
     SQL> connect SYS/oracle@ORCL as sysdba
     SQL> show user
     USER est "SYS"
     
     SQL> exec sys.kupp$proc.change_user('ZZTEST02');
     BEGIN sys.kupp$proc.change_user('ZZTEST02'); END;
     
     *
     ERROR at line 1:
     ORA-31625: Schema ZZTEST02 is needed to import this object, but is unaccessible
     ORA-06512: at "SYS.KUPP$PROC", line 45
     ORA-06512: at "SYS.KUPP$PROC", line 1006
     ORA-06512: at line 1
     
Si message d'erreur, exécuter la requete ci-dessous et recommencer.
     SQL> select sys.kupp$proc.disable_multiprocess from dual;
     DISABLE_MULTIPROCESS
     --------------------
                1
     
Hum, visiblement cela a été corrigé en 12.1.           
     SQL> exec sys.kupp$proc.change_user('ZZTEST02');
     BEGIN sys.kupp$proc.change_user('ZZTEST02'); END;
     *
     ERROR at line 1:
     ORA-31625: Schema ZZTEST02 is needed to import this object, but is unaccessible
     ORA-06512: at "SYS.KUPP$PROC", line 45
     ORA-06512: at "SYS.KUPP$PROC", line 1006
     ORA-06512: at line 1
     
On parle d'une correction ici http://www.red-database-security.com/wp/best_of_oracle_security_2011.pdf mais c'est encore KO.
     SQL> show user
     USER is "SYS"
     
     SQL> create or replace PROCEDURE  CHANGE_USER_INT (USERNAME IN VARCHAR2, MP_ENABLED IN BINARY_INTEGER) IS
     EXTERNAL NAME "kuppchus" LANGUAGE C LIBRARY KUPP_PROC_LIB WITH CONTEXT
     PARAMETERS ( CONTEXT, USERNAME STRING, USERNAME INDICATOR SB2, MP_ENABLED SB4 );
     /
     Procedure created.
      
     SQL> exec CHANGE_USER_INT('ZZTEST02',0);
     BEGIN CHANGE_USER_INT('ZZTEST02',0); END;
     *
     ERROR at line 1:
     ORA-31625: Schema ZZTEST02 is needed to import this object, but is unaccessible
     ORA-06512: at "SYS.CHANGE_USER_INT", line 1
     ORA-06512: at line 1

OK, je laisse tomber, je n'arrive pas sur une 12.1 à effectuer cette manip.
     
     
============================================================================================
Solution 11 : Privilège "BECOME USER"
============================================================================================

Les rôles DBA et IMP_FULL_DATABASE possèdent le privilège "BECOME USER" qui permet de changer d'identité.  
En effet, quand le user U1 veut faire un export/import d'un autre schéma ou de la base entière, il doit accéder aux schémas autres que le sien.
Mais je ne pourrai pas tester un changement de user via ce privilège car impossible de trouver un test simple et clair.


============================================================================================
Solution 12 : Le compte PUBLIC
============================================================================================
    
On arrive au bout de notre voyage, ce sera avec le user très spécial PUBLIC.
Il faut savoir que pour Oracle ce user est en réalité un groupe d'utilisateurs et même un rôle. Et comme chaque user appartient à ce groupe, un user peut accéder naturellement aux objets de ce user; c'est l'objet de cet article.

PUBLIC est aussi un rôle caché; il est filtré dans la définition de DBA_ROLES. Attention, pour avoir la définition d'un objet du schéma SYS, il faut se connecter au CDB$ROOT si la base 12 est en mode CDB. Vous noterez qu'un rôle est stocké dans la table des users; de là à dire qu'un rôle est un user spécial...
     SQL> create user zztest identified by TotoTiti31380000;
     SQL> grant dba to zztest;
     SQL> connect SYS/oracle@ORCL as sysdba
     SQL> create user zztest02 identified by YukioYukio31380000;
     SQL> grant dba to zztest02;

     SQL> show user

     USER is "SYS"


     SQL> alter session set container=CDB$ROOT;

     Session altered.

Voyons voir un peu ce fameux user PUBLIC. Ah oui, il est juste après SYS, avec le code 1.
     SQL> select user#, name from user$ where user# < 11;
     USER# NAME
     ---------- --
     0 SYS
     1 PUBLIC
     2 CONNECT
     3 RESOURCE
     4 DBA
     5 PDB_DBA
     6 AUDIT_ADMIN
     7 AUDIT_VIEWER
     8 AUDSYS
     9 SYSTEM
    10 SELECT_CATALOG_ROLE

    11 rows selected.

     SQL> select dbms_metadata.get_ddl('VIEW', 'DBA_ROLES') from dual;
     DBMS_METADATA.GET_DDL('VIEW','DBA_ROLES')
     --------------------------------------------------------------------------------
     CREATE OR REPLACE FORCE NONEDITIONABLE VIEW "SYS"."DBA_ROLES" ("ROLE", "ROLE_I
     D", "PASSWORD_REQUIRED", "AUTHENTICATION_TYPE", "COMMON", "ORACLE_MAINTAINED", "
     INHERITED", "IMPLICIT") AS
       select name, user#,
              decode(password, null,
                  decode(spare4, null, 'NO',
                    decode(REGEXP_INSTR(spare4, '[ST]:'), 0, 'NO',
                       'YES')),
                       'EXTERNAL',    'EXTERNAL',
                       'GLOBAL',      'GLOBAL',
                       'YES'),
              decode(password, null,
                  decode(spare4, null, 'NONE',
                    decode(REGEXP_INSTR(spare4, '[ST]:'), 0, 'NONE',
                       'PASSWORD')),
                       'EXTERNAL',    'EXTERNAL',
                       'GLOBAL',      'GLOBAL',
                       'APPLICATION', 'APPLICATION',
                       'PASSWORD'),
              decode(bitand(spare1, 4224), 0, 'NO', 'YES'),
              decode(bitand(spare1, 256), 256, 'Y', 'N'),
              decode(bitand(spare1, 4224),
                 128, decode(SYS_CONTEXT('USERENV', 'CON_ID'),
                     1, 'NO', 'YES'),
                 4224, decode(SYS_CONTEXT('USERENV', 'IS_APPLICATION_PDB'),
                      'YES', 'YES', 'NO'),
                 'NO'),
              decode(bitand(spare1, 32768), 32768, 'YES', 'NO')
     from  user$
     where type# = 0 and name not in ('PUBLIC', '_NEXT_USER')
   
Je  crée un synonyme publique, qui, comme son nom l'indique, va dans le schéma PUBLIC.          
     SQL> connect zztest;
     SQL> create table zz01(id date);
     SQL> create public synonym PUB_SYN_ZZ01 for zz01;
     
     SQL> select owner from dba_objects where object_name = 'PUB_SYN_ZZ01';
     OWNER
     ------------------------
     PUBLIC

Ici, Oracle regarde dans le schéma courant, pas dans PUBLIC puisque l'objet zz01 existe dans le schéma du user connecté.    
     SQL> select * from zz01;
     no rows selected

Les users U1 et U2 ont bien accès au synonyme du schéma PUBLIC, sans faire quoi que ce soit de spécial. Attention à bien encadrer le schéma PUBLIC par des guillemets sinon, mesage d'erreur!     
     SQL> select * from "PUBLIC".PUB_SYN_ZZ01;
     no rows selected

Nous voyons ici que le user U2 accède de deux façons différentes au schéma PUBLIC : soit en préfixant le nom d'un objet par le nom PUBLIC soit en ne le préfixant pas et Oracle va chercher à deux endroits différents, dans le schéma courant et, si l'objet n'existe pas, dans le schéma PUBLIC puisque chaque user y a accès.     
     SQL> connect zztest02
     SQL> select * from "PUBLIC".PUB_SYN_ZZ01;
     no rows selected
     
     SQL> select * from PUB_SYN_ZZ01;
     no rows selected