Repository: ucfopen/canvasapi

Issue:
Usage Rights (Files)
I would like to set usage rights (published, etc) for specific files. It seems [Files API/set_usage_rights](https://canvas.instructure.com/doc/api/files.html#method.usage_rights.set_usage_rights) should do it, but I haven't been able to figure out how to do this with canvasapi. I'm sorry if this is already implemented!

**What resource needs additional coverage?**
Usage Rights

**What endpoint needs to be covered?**
https://canvas.instructure.com/doc/api/files.html
  - [ ] [Set usage rights](https://canvas.instructure.com/doc/api/files.html#method.usage_rights.set_usage_rights)
  - [ ] [Remove usage rights](https://canvas.instructure.com/doc/api/files.html#method.usage_rights.remove_usage_rights)
  - [ ] [List Licenses](https://canvas.instructure.com/doc/api/files.html#method.usage_rights.licenses)

_New to working on CanvasAPI? Check out our [contributing guide](https://github.com/ucfopen/canvasapi/blob/develop/.github/CONTRIBUTING.md)!_


Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/26656724/badge)](https://coveralls.io/builds/26656724)

Coverage remained the same at 100.0% when pulling **fe57db64b16403ca95c8b25f022e8a00072ac6ac on atarisafari:issue/298-file-usage** into **2da024f118f23654a903abcf9750a5ae98d2669b on ucfopen:develop**.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3bde148..30c42eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Authentications Log (Thanks, [@weining-li](https://github.com/weining-li))
 - Brand Configs (Thanks, [@bennettscience](https://github.com/bennettscience))
 - Comm Messages (Thanks, [@ljoks](https://github.com/ljoks))
+- File Usage (Thanks, [@atarisafari](https://github.com/atarisafari))
 - Quiz Submission Events (Thanks, [@Goff-Davis](https://github.com/Goff-Davis))
 - Rubric Associations (Thanks, [@weining-li](https://github.com/weining-li))
 
diff --git a/canvasapi/course.py b/canvasapi/course.py
index d5ef716..3ce4983 100644
--- a/canvasapi/course.py
+++ b/canvasapi/course.py
@@ -14,6 +14,7 @@ from canvasapi.grading_period import GradingPeriod
 from canvasapi.exceptions import RequiredFieldMissing
 from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.folder import Folder
+from canvasapi.license import License
 from canvasapi.outcome_import import OutcomeImport
 from canvasapi.page import Page
 from canvasapi.paginated_list import PaginatedList
@@ -23,6 +24,7 @@ from canvasapi.tab import Tab
 from canvasapi.rubric import RubricAssociation, Rubric
 from canvasapi.submission import GroupedSubmission, Submission
 from canvasapi.upload import Uploader
+from canvasapi.usage_rights import UsageRights
 from canvasapi.util import (
     combine_kwargs,
     is_multivalued,
@@ -1328,6 +1330,26 @@ class Course(CanvasObject):
             _kwargs=combine_kwargs(**kwargs),
         )
 
+    def get_licenses(self, **kwargs):
+        """
+        Returns a paginated list of the licenses that can be applied to the
+        files under the course scope
+
+        :calls: `GET /api/v1/course/:course_id/content_licenses \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.licenses>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.license.License`
+        """
+
+        return PaginatedList(
+            License,
+            self._requester,
+            "GET",
+            "courses/{}/content_licenses".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_migration_systems(self, **kwargs):
         """
         Return a list of migration systems.
@@ -2360,6 +2382,24 @@ class Course(CanvasObject):
         )
         return response.json().get("html", "")
 
+    def remove_usage_rights(self, **kwargs):
+        """
+        Removes the usage rights for specified files that are under the current course scope
+
+        :calls: `DELETE /api/v1/courses/:course_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.remove_usage_rights>`_
+
+        :rtype: dict
+        """
+
+        response = self._requester.request(
+            "DELETE",
+            "courses/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return response.json()
+
     def reorder_pinned_topics(self, order):
         """
         Puts the pinned discussion topics in the specified order.
@@ -2458,6 +2498,24 @@ class Course(CanvasObject):
             QuizExtension(self._requester, extension) for extension in extension_list
         ]
 
+    def set_usage_rights(self, **kwargs):
+        """
+        Changes the usage rights for specified files that are under the current course scope
+
+        :calls: `PUT /api/v1/courses/:course_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.set_usage_rights>`_
+
+        :rtype: :class:`canvasapi.usage_rights.UsageRights`
+        """
+
+        response = self._requester.request(
+            "PUT",
+            "courses/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return UsageRights(self._requester, response.json())
+
     def show_front_page(self):
         """
         Retrieve the content of the front page.
diff --git a/canvasapi/group.py b/canvasapi/group.py
index 1804509..758a8a2 100644
--- a/canvasapi/group.py
+++ b/canvasapi/group.py
@@ -9,8 +9,10 @@ from canvasapi.collaboration import Collaboration
 from canvasapi.discussion_topic import DiscussionTopic
 from canvasapi.folder import Folder
 from canvasapi.exceptions import RequiredFieldMissing
+from canvasapi.license import License
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.tab import Tab
+from canvasapi.usage_rights import UsageRights
 from canvasapi.util import combine_kwargs, is_multivalued, obj_or_id
 
 
@@ -563,6 +565,26 @@ class Group(CanvasObject):
         )
         return response.json()
 
+    def get_licenses(self, **kwargs):
+        """
+        Returns a paginated list of the licenses that can be applied to the
+        files under the group scope
+
+        :calls: `GET /api/v1/groups/:group_id/content_licenses \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.licenses>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.license.License`
+        """
+
+        return PaginatedList(
+            License,
+            self._requester,
+            "GET",
+            "groups/{}/content_licenses".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_membership(self, user, membership_type):
         """
         List users in a group.
@@ -879,6 +901,23 @@ class Group(CanvasObject):
         )
         return response.json().get("html", "")
 
+    def remove_usage_rights(self, **kwargs):
+        """
+        Removes the usage rights for specified files that are under the current group scope
+
+        :calls: `DELETE /api/v1/groups/:group_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.remove_usage_rights>`_
+
+        :rtype: dict
+        """
+        response = self._requester.request(
+            "DELETE",
+            "groups/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return response.json()
+
     def remove_user(self, user):
         """
         Leave a group if allowed.
@@ -929,6 +968,24 @@ class Group(CanvasObject):
 
         return response.json().get("reorder")
 
+    def set_usage_rights(self, **kwargs):
+        """
+        Changes the usage rights for specified files that are under the current group scope
+
+        :calls: `PUT /api/v1/groups/:group_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.set_usage_rights>`_
+
+        :rtype: :class:`canvasapi.usage_rights.UsageRights`
+        """
+
+        response = self._requester.request(
+            "PUT",
+            "groups/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return UsageRights(self._requester, response.json())
+
     def show_front_page(self):
         """
         Retrieve the content of the front page.
diff --git a/canvasapi/license.py b/canvasapi/license.py
new file mode 100644
index 0000000..2400e29
--- /dev/null
+++ b/canvasapi/license.py
@@ -0,0 +1,11 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from six import python_2_unicode_compatible
+
+from canvasapi.canvas_object import CanvasObject
+
+
+@python_2_unicode_compatible
+class License(CanvasObject):
+    def __str__(self):
+        return "{} {}".format(self.name, self.id)
diff --git a/canvasapi/usage_rights.py b/canvasapi/usage_rights.py
new file mode 100644
index 0000000..1de8adb
--- /dev/null
+++ b/canvasapi/usage_rights.py
@@ -0,0 +1,11 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from six import python_2_unicode_compatible
+
+from canvasapi.canvas_object import CanvasObject
+
+
+@python_2_unicode_compatible
+class UsageRights(CanvasObject):
+    def __str__(self):
+        return "{} {}".format(self.use_justification, self.license)
diff --git a/canvasapi/user.py b/canvasapi/user.py
index 92ee93a..116727c 100644
--- a/canvasapi/user.py
+++ b/canvasapi/user.py
@@ -9,7 +9,9 @@ from canvasapi.communication_channel import CommunicationChannel
 from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.folder import Folder
 from canvasapi.paginated_list import PaginatedList
+from canvasapi.license import License
 from canvasapi.upload import Uploader
+from canvasapi.usage_rights import UsageRights
 from canvasapi.util import combine_kwargs, obj_or_id, obj_or_str
 
 
@@ -585,6 +587,26 @@ class User(CanvasObject):
             _kwargs=combine_kwargs(**kwargs),
         )
 
+    def get_licenses(self, **kwargs):
+        """
+        Returns a paginated list of the licenses that can be applied to the
+        files under the user scope
+
+        :calls: `GET /api/v1/users/:user_id/content_licenses \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.licenses>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.license.License`
+        """
+
+        return PaginatedList(
+            License,
+            self._requester,
+            "GET",
+            "users/{}/content_licenses".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_migration_systems(self, **kwargs):
         """
         Return a list of migration systems.
@@ -890,6 +912,42 @@ class User(CanvasObject):
         )
         return User(self._requester, response.json())
 
+    def remove_usage_rights(self, **kwargs):
+        """
+        Changes the usage rights for specified files that are under the user scope
+
+        :calls: `DELETE /api/v1/users/:user_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.remove_usage_rights>`_
+
+        :rtype: dict
+        """
+
+        response = self._requester.request(
+            "DELETE",
+            "users/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return response.json()
+
+    def set_usage_rights(self, **kwargs):
+        """
+        Changes the usage rights for specified files that are under the user scope
+
+        :calls: `PUT /api/v1/users/:user_id/usage_rights \
+        <https://canvas.instructure.com/doc/api/files.html#method.usage_rights.set_usage_rights>`_
+
+        :rtype: :class:`canvasapi.usage_rights.UsageRights`
+        """
+
+        response = self._requester.request(
+            "PUT",
+            "users/{}/usage_rights".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return UsageRights(self._requester, response.json())
+
     def show_observee(self, observee_id):
         """
         Gets information about an observed user.
diff --git a/docs/class-reference.rst b/docs/class-reference.rst
index b475a9b..4da3343 100644
--- a/docs/class-reference.rst
+++ b/docs/class-reference.rst
@@ -34,6 +34,7 @@ Class Reference
     grading-period-ref
     group-ref
     login-ref
+    license-ref
     module-ref
     outcome-ref
     outcome-import-ref
@@ -54,3 +55,4 @@ Class Reference
     tab-ref
     upload-ref
     user-ref
+    usage-rights-ref
diff --git a/docs/license-ref.rst b/docs/license-ref.rst
new file mode 100644
index 0000000..17eaa79
--- /dev/null
+++ b/docs/license-ref.rst
@@ -0,0 +1,6 @@
+=======
+License
+=======
+
+.. autoclass:: canvasapi.license.License
+    :members:
\ No newline at end of file
diff --git a/docs/usage-rights-ref.rst b/docs/usage-rights-ref.rst
new file mode 100644
index 0000000..efeb780
--- /dev/null
+++ b/docs/usage-rights-ref.rst
@@ -0,0 +1,6 @@
+===========
+UsageRights
+===========
+
+.. autoclass:: canvasapi.usage_rights.UsageRights
+    :members:
\ No newline at end of file


Test patch:
diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json
index c65a1de..f00bb64 100644
--- a/tests/fixtures/course.json
+++ b/tests/fixtures/course.json
@@ -1919,5 +1919,42 @@
 			"association_type": "Course"
 		},
 		"status_code": 200
+	},
+	"set_usage_rights": {
+		"method": "PUT",
+		"endpoint": "courses/1/usage_rights",
+		"data": {
+			"use_justification": "fair_use",
+			"license": "private",
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"remove_usage_rights": {
+		"method": "DELETE",
+		"endpoint": "courses/1/usage_rights",
+		"data": {
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"get_licenses": {
+		"method": "GET",
+		"endpoint": "courses/1/content_licenses",
+		"data": [
+			{
+				"id": "private",
+				"name": "Private (Copyrighted)",
+				"url": "http://en.wikipedia.org/wiki/Copyright"
+			},
+			{
+				"id": "public_domain",
+				"name": "Public domain",
+				"url": "http://en.wikipedia.org/wiki/Public_domain"
+			}
+		],
+		"status_code": 200
 	}
 }
diff --git a/tests/fixtures/group.json b/tests/fixtures/group.json
index f34d68f..57af19e 100644
--- a/tests/fixtures/group.json
+++ b/tests/fixtures/group.json
@@ -946,5 +946,42 @@
 			"export_type": "d"
 		},
 		"status_code": 200
+	},
+	"set_usage_rights": {
+		"method": "PUT",
+		"endpoint": "groups/1/usage_rights",
+		"data": {
+			"use_justification": "fair_use",
+			"license": "private",
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"remove_usage_rights": {
+		"method": "DELETE",
+		"endpoint": "groups/1/usage_rights",
+		"data": {
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"get_licenses": {
+		"method": "GET",
+		"endpoint": "groups/1/content_licenses",
+		"data": [
+			{
+				"id": "private",
+				"name": "Private (Copyrighted)",
+				"url": "http://en.wikipedia.org/wiki/Copyright"
+			},
+			{
+				"id": "public_domain",
+				"name": "Public domain",
+				"url": "http://en.wikipedia.org/wiki/Public_domain"
+			}
+		],
+		"status_code": 200
 	}
 }
diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json
index bf40e1e..2a41536 100644
--- a/tests/fixtures/user.json
+++ b/tests/fixtures/user.json
@@ -935,5 +935,42 @@
 			}
 		],
 		"status_code": 200
+	},
+	"set_usage_rights": {
+		"method": "PUT",
+		"endpoint": "users/1/usage_rights",
+		"data": {
+			"use_justification": "fair_use",
+			"license": "private",
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"remove_usage_rights": {
+		"method": "DELETE",
+		"endpoint": "users/1/usage_rights",
+		"data": {
+			"message": "2 files updated",
+			"file_ids": [1,2]
+		},
+		"status_code": 200
+	},
+	"get_licenses": {
+		"method": "GET",
+		"endpoint": "users/1/content_licenses",
+		"data": [
+			{
+				"id": "private",
+				"name": "Private (Copyrighted)",
+				"url": "http://en.wikipedia.org/wiki/Copyright"
+			},
+			{
+				"id": "public_domain",
+				"name": "Public domain",
+				"url": "http://en.wikipedia.org/wiki/Public_domain"
+			}
+		],
+		"status_code": 200
 	}
 }
diff --git a/tests/test_course.py b/tests/test_course.py
index 55cdc87..0d1a5ee 100644
--- a/tests/test_course.py
+++ b/tests/test_course.py
@@ -25,6 +25,7 @@ from canvasapi.file import File
 from canvasapi.folder import Folder
 from canvasapi.grading_period import GradingPeriod
 from canvasapi.group import Group, GroupCategory
+from canvasapi.license import License
 from canvasapi.module import Module
 from canvasapi.outcome import OutcomeGroup, OutcomeLink
 from canvasapi.outcome_import import OutcomeImport
@@ -37,6 +38,7 @@ from canvasapi.submission import GroupedSubmission, Submission
 from canvasapi.tab import Tab
 from canvasapi.user import User
 from canvasapi.user import UserDisplay
+from canvasapi.usage_rights import UsageRights
 from canvasapi.content_migration import ContentMigration, Migrator
 from canvasapi.content_export import ContentExport
 from tests import settings
@@ -1779,6 +1781,48 @@ class TestCourse(unittest.TestCase):
         self.assertEqual(rubric_association.id, 4)
         self.assertEqual(rubric_association.association_type, "Course")
 
+    # set_usage_rights()
+    def test_set_usage_rights(self, m):
+        register_uris({"course": ["set_usage_rights"]}, m)
+
+        usage_rights = self.course.set_usage_rights(
+            file_ids=[1, 2],
+            usage_rights={"use_justification": "fair_use", "license": "private"},
+        )
+
+        self.assertIsInstance(usage_rights, UsageRights)
+        self.assertEqual(usage_rights.use_justification, "fair_use")
+        self.assertEqual(usage_rights.message, "2 files updated")
+        self.assertEqual(usage_rights.license, "private")
+        self.assertEqual(usage_rights.file_ids, [1, 2])
+
+    # remove_usage_rights()
+    def test_remove_usage_rights(self, m):
+        register_uris({"course": ["remove_usage_rights"]}, m)
+
+        retval = self.course.remove_usage_rights(file_ids=[1, 2])
+
+        self.assertIsInstance(retval, dict)
+        self.assertIn("message", retval)
+        self.assertEqual(retval["file_ids"], [1, 2])
+        self.assertEqual(retval["message"], "2 files updated")
+
+    # get_licenses()
+    def test_get_licenses(self, m):
+        register_uris({"course": ["get_licenses"]}, m)
+
+        licenses = self.course.get_licenses()
+        self.assertIsInstance(licenses, PaginatedList)
+        licenses = list(licenses)
+
+        for l in licenses:
+            self.assertIsInstance(l, License)
+            self.assertTrue(hasattr(l, "id"))
+            self.assertTrue(hasattr(l, "name"))
+            self.assertTrue(hasattr(l, "url"))
+
+        self.assertEqual(2, len(licenses))
+
 
 @requests_mock.Mocker()
 class TestCourseNickname(unittest.TestCase):
diff --git a/tests/test_group.py b/tests/test_group.py
index 48c1ad3..1adb489 100644
--- a/tests/test_group.py
+++ b/tests/test_group.py
@@ -16,10 +16,12 @@ from canvasapi.exceptions import RequiredFieldMissing
 from canvasapi.external_feed import ExternalFeed
 from canvasapi.file import File
 from canvasapi.folder import Folder
+from canvasapi.license import License
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.tab import Tab
 from canvasapi.content_migration import ContentMigration, Migrator
 from canvasapi.content_export import ContentExport
+from canvasapi.usage_rights import UsageRights
 from tests import settings
 from tests.util import cleanup_file, register_uris
 
@@ -620,6 +622,48 @@ class TestGroup(unittest.TestCase):
         self.assertIsInstance(content_export, ContentExport)
         self.assertTrue(hasattr(content_export, "export_type"))
 
+    # set_usage_rights()
+    def test_set_usage_rights(self, m):
+        register_uris({"group": ["set_usage_rights"]}, m)
+
+        usage_rights = self.group.set_usage_rights(
+            file_ids=[1, 2],
+            usage_rights={"use_justification": "fair_use", "license": "private"},
+        )
+
+        self.assertIsInstance(usage_rights, UsageRights)
+        self.assertEqual(usage_rights.use_justification, "fair_use")
+        self.assertEqual(usage_rights.message, "2 files updated")
+        self.assertEqual(usage_rights.license, "private")
+        self.assertEqual(usage_rights.file_ids, [1, 2])
+
+    # remove_usage_rights()
+    def test_remove_usage_rights(self, m):
+        register_uris({"group": ["remove_usage_rights"]}, m)
+
+        retval = self.group.remove_usage_rights(file_ids=[1, 2])
+
+        self.assertIsInstance(retval, dict)
+        self.assertIn("message", retval)
+        self.assertEqual(retval["file_ids"], [1, 2])
+        self.assertEqual(retval["message"], "2 files updated")
+
+    # get_licenses()
+    def test_get_licenses(self, m):
+        register_uris({"group": ["get_licenses"]}, m)
+
+        licenses = self.group.get_licenses()
+        self.assertIsInstance(licenses, PaginatedList)
+        licenses = list(licenses)
+
+        for l in licenses:
+            self.assertIsInstance(l, License)
+            self.assertTrue(hasattr(l, "id"))
+            self.assertTrue(hasattr(l, "name"))
+            self.assertTrue(hasattr(l, "url"))
+
+        self.assertEqual(2, len(licenses))
+
 
 @requests_mock.Mocker()
 class TestGroupMembership(unittest.TestCase):
diff --git a/tests/test_licenses.py b/tests/test_licenses.py
new file mode 100644
index 0000000..f5359f8
--- /dev/null
+++ b/tests/test_licenses.py
@@ -0,0 +1,27 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+import unittest
+
+import requests_mock
+
+from canvasapi import Canvas
+from tests import settings
+from tests.util import register_uris
+
+
+@requests_mock.Mocker()
+class TestLicenses(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            requires = {"user": ["get_by_id", "get_licenses"]}
+
+            register_uris(requires, m)
+
+            self.user = self.canvas.get_user(1)
+            self.licenses = list(self.user.get_licenses())
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.licenses[0])
+        self.assertIsInstance(string, str)
diff --git a/tests/test_usage_rights.py b/tests/test_usage_rights.py
new file mode 100644
index 0000000..50256e1
--- /dev/null
+++ b/tests/test_usage_rights.py
@@ -0,0 +1,29 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+import unittest
+
+import requests_mock
+
+from canvasapi import Canvas
+from tests import settings
+from tests.util import register_uris
+
+
+@requests_mock.Mocker()
+class TestUsageRights(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            requires = {"user": ["get_by_id", "set_usage_rights"]}
+
+            register_uris(requires, m)
+
+            self.user = self.canvas.get_user(1)
+            self.usage_rights = self.user.set_usage_rights(
+                file_ids=[1, 2], usage_rights={"use_justification": "fair_use"}
+            )
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.usage_rights)
+        self.assertIsInstance(string, str)
diff --git a/tests/test_user.py b/tests/test_user.py
index 6a32251..bd6b3bb 100644
--- a/tests/test_user.py
+++ b/tests/test_user.py
@@ -19,9 +19,11 @@ from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.file import File
 from canvasapi.folder import Folder
 from canvasapi.login import Login
+from canvasapi.license import License
 from canvasapi.page_view import PageView
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.user import User
+from canvasapi.usage_rights import UsageRights
 from tests import settings
 from tests.util import cleanup_file, register_uris
 
@@ -581,6 +583,48 @@ class TestUser(unittest.TestCase):
         self.assertEqual(event_list[1].created_at, "2012-07-20T15:00:00-06:00")
         self.assertEqual(event_list[1].event_type, "logout")
 
+    # set_usage_rights()
+    def test_set_usage_rights(self, m):
+        register_uris({"user": ["set_usage_rights"]}, m)
+
+        usage_rights = self.user.set_usage_rights(
+            file_ids=[1, 2],
+            usage_rights={"use_justification": "fair_use", "license": "private"},
+        )
+
+        self.assertIsInstance(usage_rights, UsageRights)
+        self.assertEqual(usage_rights.use_justification, "fair_use")
+        self.assertEqual(usage_rights.message, "2 files updated")
+        self.assertEqual(usage_rights.license, "private")
+        self.assertEqual(usage_rights.file_ids, [1, 2])
+
+    # remove_usage_rights()
+    def test_remove_usage_rights(self, m):
+        register_uris({"user": ["remove_usage_rights"]}, m)
+
+        retval = self.user.remove_usage_rights(file_ids=[1, 2])
+
+        self.assertIsInstance(retval, dict)
+        self.assertIn("message", retval)
+        self.assertEqual(retval["file_ids"], [1, 2])
+        self.assertEqual(retval["message"], "2 files updated")
+
+    # get_licenses()
+    def test_get_licenses(self, m):
+        register_uris({"user": ["get_licenses"]}, m)
+
+        licenses = self.user.get_licenses()
+        self.assertIsInstance(licenses, PaginatedList)
+        licenses = list(licenses)
+
+        for l in licenses:
+            self.assertIsInstance(l, License)
+            self.assertTrue(hasattr(l, "id"))
+            self.assertTrue(hasattr(l, "name"))
+            self.assertTrue(hasattr(l, "url"))
+
+        self.assertEqual(2, len(licenses))
+
 
 @requests_mock.Mocker()
 class TestUserDisplay(unittest.TestCase):