Skip to content

User and Role Management in Oracle

Overview

In this workshop you will:
- Build a small HR schema (departments, employees, salaries).
- Create common users for admins, managers and employees.
- Protect data with views and privileges, then simplify with roles.
- Verify that each user sees only what they should.

All commands target Oracle Database (for example via SQL*Plus on Linux).

Notation
- Terminal: commands in bash blocks (prompt example: [oracle@dbserver ~]$).
- SQL*Plus: commands in sql blocks (run after connecting with sqlplus).


1. Scenario and data model

The HR team needs three access levels:
- Administrator (ADMIN): manages users/roles and has full access to HR data.
- Manager (MANAGER): can view and update basic employee info only inside their department (name, email, department). No salaries. Cannot manage users.
- Employee (EMPLOYEE): can only view employee names.

Each manager/employee is an Oracle user (sufficient for a small lab).

Tables:
- DEPARTMENTS(DEPT_ID, DEPT_NAME) - list of departments.
- EMPLOYEES(EMP_ID, NAME, DEPT_ID, MANAGER_OF, USERNAME, EMAIL, JOIN_DATE) - staff and which department they manage (MANAGER_OF is null for non-managers).
- SALARIES(EMP_ID, SALARY) - salary isolated for tighter control.


2. Prepare the admin account and schema

2.1 Connect as SYSDBA

Terminal ([oracle@dbserver ~]$):

sqlplus / AS SYSDBA

2.2 Create the admin user (adjust the password to your policy)

CREATE USER c##admin IDENTIFIED BY admin
  DEFAULT TABLESPACE users
  QUOTA UNLIMITED ON users;

GRANT DBA TO c##admin;

Connect as admin:

CONNECT c##admin/admin

2.3 Create the HR tables

CREATE TABLE departments (
  dept_id    NUMBER PRIMARY KEY,
  dept_name  VARCHAR2(100)
);

CREATE TABLE employees (
  emp_id      NUMBER PRIMARY KEY,
  name        VARCHAR2(50),
  dept_id     NUMBER REFERENCES departments(dept_id),
  manager_of  NUMBER REFERENCES departments(dept_id), -- which department this user manages
  username    VARCHAR2(30),                           -- Oracle username
  email       VARCHAR2(100),
  join_date   DATE
);

CREATE TABLE salaries (
  emp_id  NUMBER PRIMARY KEY,
  salary  NUMBER,
  FOREIGN KEY (emp_id) REFERENCES employees(emp_id)
);

2.4 Load sample data

INSERT INTO departments VALUES (20, 'Sales');
INSERT INTO departments VALUES (10, 'IT');

INSERT INTO employees (emp_id, name, dept_id, manager_of, email, join_date, username)
VALUES (1, 'Ahmed', 20, 20, 'ahmed@sales.company.com', DATE '2020-05-10', 'C##MANAGER1');

INSERT INTO employees (emp_id, name, dept_id, manager_of, email, join_date, username)
VALUES (2, 'Ali', 10, NULL, 'ali@it.company.com', DATE '2021-01-15', 'C##EMP1');

INSERT INTO employees (emp_id, name, dept_id, manager_of, email, join_date, username)
VALUES (3, 'Amir', 10, 10, 'amir@it.company.com', DATE '2019-03-22', 'C##MANAGER2');

INSERT INTO employees (emp_id, name, dept_id, manager_of, email, join_date, username)
VALUES (4, 'Imad', 20, NULL, 'imad@sales.company.com', DATE '2022-07-01', 'C##EMP2');

INSERT INTO employees (emp_id, name, dept_id, manager_of, email, join_date, username)
VALUES (5, 'Raouf', 10, NULL, 'raouf@it.company.com', DATE '2023-09-05', 'C##EMP3');

INSERT INTO salaries (emp_id, salary) VALUES (1, 8500.00);
INSERT INTO salaries (emp_id, salary) VALUES (2, 4200.00);
INSERT INTO salaries (emp_id, salary) VALUES (3, 9200.00);
INSERT INTO salaries (emp_id, salary) VALUES (4, 4800.00);
INSERT INTO salaries (emp_id, salary) VALUES (5, 3900.00);

Managers C##MANAGER1 and C##MANAGER2 manage departments 10 and 20 via the MANAGER_OF column.


3. Create the Oracle users

Run as C##ADMIN:

CREATE USER c##manager1 IDENTIFIED BY manager1;
GRANT CREATE SESSION TO c##manager1;

CREATE USER c##manager2 IDENTIFIED BY manager2;
GRANT CREATE SESSION TO c##manager2;

CREATE USER c##emp1 IDENTIFIED BY emp1;
GRANT CREATE SESSION TO c##emp1;

CREATE USER c##emp2 IDENTIFIED BY emp2;
GRANT CREATE SESSION TO c##emp2;

CREATE USER c##emp3 IDENTIFIED BY emp3;
GRANT CREATE SESSION TO c##emp3;

At this point they can log in but cannot see tables.


4. Version 1: direct privileges via views

4.1 Department-scoped manager view

Limit managers to their own department based on the USERNAME in EMPLOYEES:

CREATE OR REPLACE VIEW VempManager AS
SELECT e.name, e.email, e.dept_id
FROM   employees e
WHERE  e.dept_id = (
  SELECT manager_of
  FROM   employees
  WHERE  username = USER
    AND  manager_of IS NOT NULL
);

Grant managers access:

GRANT SELECT, UPDATE ON VempManager TO c##manager1;
GRANT SELECT, UPDATE ON VempManager TO c##manager2;
GRANT SELECT ON departments TO c##manager1;
GRANT SELECT ON departments TO c##manager2;

4.2 Employee names view

Expose only names to employees:

CREATE OR REPLACE VIEW emp_names AS
SELECT name FROM employees;

Grant read-only access:

GRANT SELECT ON emp_names TO c##emp1;
GRANT SELECT ON emp_names TO c##emp2;
GRANT SELECT ON emp_names TO c##emp3;

Result so far:
- Managers can view/update basic data in their own department plus see departments.
- Employees can read only the list of names.
- Salaries remain hidden for everyone except C##ADMIN.


5. Version 2: wrap privileges in roles

Roles make it easy to onboard new users by granting a single object.

5.1 Manager role

CREATE ROLE C##r_manager;
GRANT SELECT, UPDATE ON VempManager TO C##r_manager;
GRANT SELECT ON departments TO C##r_manager;

GRANT C##r_manager TO c##manager1;
GRANT C##r_manager TO c##manager2;

5.2 Employee role

CREATE ROLE C##r_employee;
GRANT SELECT ON emp_names TO C##r_employee;

GRANT C##r_employee TO c##emp1;
GRANT C##r_employee TO c##emp2;
GRANT C##r_employee TO c##emp3;

6. Verify as SYSDBA

Run these checks to confirm the setup:

-- Tables
SELECT owner, table_name
FROM   dba_tables
WHERE  table_name IN ('EMPLOYEES','DEPARTMENTS','SALARIES');

-- Views
SELECT owner, view_name
FROM   dba_views
WHERE  view_name IN ('VEMPMANAGER','EMP_NAMES');

-- Users
SELECT username, account_status, created
FROM   dba_users
WHERE  username LIKE 'C##%';

-- Roles granted to users
SELECT grantee, granted_role
FROM   dba_role_privs
WHERE  grantee LIKE 'C##%';

-- Privileges of a specific role
SELECT * FROM dba_tab_privs WHERE grantee = 'C##R_MANAGER';
SELECT * FROM dba_tab_privs WHERE grantee = 'C##R_EMPLOYEE';

7. Test as each user

7.1 Admin (C##ADMIN)

CONNECT c##admin/admin
SELECT table_name FROM user_tables;
SELECT view_name  FROM user_views;
SELECT * FROM salaries; -- full access

7.2 Manager (C##MANAGER1 / C##MANAGER2)

CONNECT c##manager1/manager1
SELECT * FROM c##admin.VempManager;     -- only their department
SELECT * FROM c##admin.departments;

UPDATE c##admin.VempManager
SET    email = 'new.mail@company.com'
WHERE  name = 'Ali';

SELECT * FROM session_roles;   -- should list C##R_MANAGER
SELECT * FROM user_tab_privs;  -- check table/view privileges

SELECT * FROM c##admin.salaries; -- should fail (ORA-01031)

7.3 Employee (C##EMP1 / C##EMP2 / C##EMP3)

CONNECT c##emp1/emp1
SELECT * FROM c##admin.emp_names;

SELECT * FROM session_roles;   -- should list C##R_EMPLOYEE
SELECT * FROM user_tab_privs;

SELECT * FROM c##admin.employees; -- should fail

8. Takeaways

  • Store Oracle usernames in the data model to drive view filters (department-scoped access).
  • Use views (VempManager, emp_names) to hide sensitive columns.
  • Granting through roles (C##R_MANAGER, C##R_EMPLOYEE) keeps onboarding and audits simple.
  • Dictionary views (DBA_%, SESSION_ROLES, USER_TAB_PRIVS) let you verify exactly who can do what.