Repository: CSCfi/rems
Pull request #2973 (2022-06-30 11:10:33)

Workflow licenses
relates #2158

Add support for creating/editing licenses into workflows. Workflow licenses have existed before in some form ( `workflow_licenses` table), so this needed some migration work. Application model enrichment is updated to use workflow licenses.

some larger changes:
- removed `workflow_licenses` table and related queries
- migrated data to `workflow` table json column

api changes:
- remove `licenses` from workflow (it is now under `[:workflow :licenses]`)

feature:
- update workflow DB layer to serialize/deserialize licenses
- update workflow service layer to join license dependencies
- create UI to allow administration to create and edit workflows to include licenses
- update workflow API & related commands to accept and validate licenses
- update application model to enrich workflow licenses
- update dependencies to compute workflow licenses

# Checklist for author

Remove items that aren't applicable, check items that are done.

## Reviewability
- [x] Link to issue
- [x] Consider adding screenshots for ease of review

## Backwards compatibility
- [ ] API is backwards compatible or completely new
- [ ] Feature appears correctly in PDF print

## Documentation
- [x] Update changelog if necessary
- [ ] API is documented and shows up in Swagger UI

## Testing
- [x] Complex logic is unit tested
- [x] Valuable features are integration / browser / acceptance tested automatically

## Follow-up
- [ ] New tasks are created for pending or remaining tasks

---
## Screenshots

![Screenshot 2022-07-18 at 14 39 34](https://user-images.githubusercontent.com/794087/179504138-4769941f-edfb-46de-9dec-79b6273e2228.png)

edit workflow
<img width="1188" alt="Screenshot 2022-09-06 at 16 42 33" src="https://user-images.githubusercontent.com/794087/188650704-f860f778-1a2c-432b-844c-85d032d5eafa.png">



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96d5ebffc..97b028c4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,10 +15,15 @@ have notable changes.
 
 Changes since v2.28
 
+**NB: This release contains migrations!**
+**NB: `workflow_licenses` table has been removed and the data is migrated to `workflow` table.**
+
+### Additions
+- Licenses can now be added to workflows through user interface and API. Workflow licenses are included in applications, similar to resource licenses. (#2158)
+
 ## v2.28 "Porkkalankatu" 2022-08-24
 
 **NB: This release contains migrations!**
-
 **NB: One of the migrations fixes organizations, that could be broken by previous features.**
 
 ### Additions
diff --git a/resources/migrations/20220621104646-refactor-workflow-licenses.edn b/resources/migrations/20220621104646-refactor-workflow-licenses.edn
new file mode 100644
index 000000000..0b83d43ff
--- /dev/null
+++ b/resources/migrations/20220621104646-refactor-workflow-licenses.edn
@@ -0,0 +1,3 @@
+{:ns rems.migrations.refactor-workflow-licenses
+ :up-fn migrate-up
+ :down-fn nil}
diff --git a/resources/migrations/20220711142631-remove-workflow-licenses-table.down.sql b/resources/migrations/20220711142631-remove-workflow-licenses-table.down.sql
new file mode 100644
index 000000000..157dc37f1
--- /dev/null
+++ b/resources/migrations/20220711142631-remove-workflow-licenses-table.down.sql
@@ -0,0 +1,7 @@
+CREATE TABLE if not exists workflow_licenses (
+  id serial NOT NULL PRIMARY KEY,
+  wfId integer DEFAULT NULL,
+  licId integer DEFAULT NULL,
+  CONSTRAINT workflow_licenses_ibfk_1 FOREIGN KEY (wfId) REFERENCES workflow (id),
+  CONSTRAINT workflow_licenses_ibfk_2 FOREIGN KEY (licId) REFERENCES license (id)
+);
diff --git a/resources/migrations/20220711142631-remove-workflow-licenses-table.up.sql b/resources/migrations/20220711142631-remove-workflow-licenses-table.up.sql
new file mode 100644
index 000000000..6f848151b
--- /dev/null
+++ b/resources/migrations/20220711142631-remove-workflow-licenses-table.up.sql
@@ -0,0 +1,1 @@
+DROP TABLE IF EXISTS workflow_licenses;
diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql
index ce4776e8d..7b3477b8b 100644
--- a/resources/sql/queries.sql
+++ b/resources/sql/queries.sql
@@ -405,12 +405,6 @@ SET
   id = id
 WHERE id = :id;
 
--- :name create-workflow-license! :insert
-INSERT INTO workflow_licenses
-(wfid, licid)
-VALUES
-(:wfid, :licid);
-
 -- TODO: consider renaming this to link-resource-license!
 -- :name create-resource-license! :insert
 INSERT INTO resource_licenses
@@ -418,11 +412,6 @@ INSERT INTO resource_licenses
 VALUES
 (:resid, :licid);
 
--- :name get-workflow-licenses :? :*
-SELECT licid
-FROM workflow_licenses
-WHERE wfid = :wfid
-
 -- :name get-workflow :? :1
 SELECT
   wf.id, wf.organization, wf.title,
@@ -448,16 +437,10 @@ FROM workflow wf;
 
 -- :name get-licenses :? :*
 -- :doc
--- - Gets application licenses by workflow and catalogue item ids
--- - :wfid workflow id for workflow licenses
+-- - Gets application licenses by catalogue item ids
 -- - :items vector of catalogue item ids for resource licenses
 SELECT lic.id, lic.type, lic.enabled, lic.archived, lic.organization
 FROM license lic
-INNER JOIN workflow_licenses wl ON lic.id = wl.licid
-WHERE wl.wfid = :wfid
-UNION
-SELECT lic.id, lic.type, lic.enabled, lic.archived, lic.organization
-FROM license lic
 INNER JOIN resource_licenses rl ON lic.id = rl.licid
 INNER JOIN catalogue_item item ON (item.resid = rl.resid)
 WHERE item.id IN (:v*:items)
@@ -485,9 +468,6 @@ FROM license_localization;
 -- :name get-resources-for-license :? :*
 SELECT resid FROM resource_licenses WHERE licid = :id;
 
--- :name get-workflows-for-license :? :*
-SELECT wfid FROM workflow_licenses WHERE licid = :id;
-
 -- :name get-all-roles :? :*
 SELECT userid, role
 FROM roles;
diff --git a/src/clj/rems/api/schema.clj b/src/clj/rems/api/schema.clj
index 3d27ea2e2..63c33d9b1 100644
--- a/src/clj/rems/api/schema.clj
+++ b/src/clj/rems/api/schema.clj
@@ -107,22 +107,22 @@
    (s/optional-key :resource/duo) {:duo/codes [schema-base/DuoCodeFull]}})
 
 (s/defschema V2License
-  {:license/id s/Int
-   :license/type (s/enum :text :link :attachment)
-   :license/title schema-base/LocalizedString
-   (s/optional-key :license/link) schema-base/LocalizedString
-   (s/optional-key :license/text) schema-base/LocalizedString
-   (s/optional-key :license/attachment-id) schema-base/LocalizedInt
-   (s/optional-key :license/attachment-filename) schema-base/LocalizedString
-   :license/enabled s/Bool
-   :license/archived s/Bool})
+  (merge
+   schema-base/LicenseId
+   {:license/type (s/enum :text :link :attachment)
+    :license/title schema-base/LocalizedString
+    (s/optional-key :license/link) schema-base/LocalizedString
+    (s/optional-key :license/text) schema-base/LocalizedString
+    (s/optional-key :license/attachment-id) schema-base/LocalizedInt
+    (s/optional-key :license/attachment-filename) schema-base/LocalizedString
+    :license/enabled s/Bool
+    :license/archived s/Bool}))
 
 (s/defschema Workflow
   {:id s/Int
    :organization schema-base/OrganizationOverview
    :title s/Str
    :workflow s/Any
-   :licenses [License]
    :enabled s/Bool
    :archived s/Bool})
 
diff --git a/src/clj/rems/api/services/catalogue.clj b/src/clj/rems/api/services/catalogue.clj
index 7d178ee3b..32eff6a47 100644
--- a/src/clj/rems/api/services/catalogue.clj
+++ b/src/clj/rems/api/services/catalogue.clj
@@ -35,7 +35,7 @@
     (-> item
         organizations/join-organization
         (update :categories category/enrich-categories)
-         ;; not used at the moment
+        ;; XXX: not used at the moment
         #_licenses/join-catalogue-item-licenses
         #_(transform [:licenses ALL] organizations/join-organization))))
 
diff --git a/src/clj/rems/api/services/dependencies.clj b/src/clj/rems/api/services/dependencies.clj
index f0d2d2ff2..1c0313546 100644
--- a/src/clj/rems/api/services/dependencies.clj
+++ b/src/clj/rems/api/services/dependencies.clj
@@ -50,8 +50,9 @@
 
    (for [workflow (workflow/get-workflows {})
          dep (concat
-              (mapv (fn [lic] {:license/id (:id lic)}) (:licenses workflow))
-              (:forms (:workflow workflow))
+              (->> (get-in workflow [:workflow :licenses])
+                   (mapv #(select-keys % [:license/id])))
+              (get-in workflow [:workflow :forms])
               [(:organization workflow)])]
      {:from {:workflow/id (:id workflow)} :to dep})
 
diff --git a/src/clj/rems/api/services/workflow.clj b/src/clj/rems/api/services/workflow.clj
index 88ac7c7dd..e57a017c8 100644
--- a/src/clj/rems/api/services/workflow.clj
+++ b/src/clj/rems/api/services/workflow.clj
@@ -1,58 +1,52 @@
 (ns rems.api.services.workflow
-  (:require [com.rpl.specter :refer [ALL transform]]
-            [rems.api.services.dependencies :as dependencies]
+  (:require [rems.api.services.dependencies :as dependencies]
             [rems.api.services.util :as util]
             [rems.db.applications :as applications]
             [rems.db.core :as db]
             [rems.db.form :as form]
+            [rems.db.licenses :as licenses]
             [rems.db.organizations :as organizations]
             [rems.db.users :as users]
-            [rems.db.workflow :as workflow]
-            [rems.json :as json]))
+            [rems.db.workflow :as workflow]))
 
 (defn invalid-forms-error [forms]
-  (let [invalid (seq (remove (comp form/get-form-template :form/id) forms))]
-    (when invalid
-      {:success false
-       :errors [{:type :invalid-form
-                 :forms invalid}]})))
+  (when-some [invalid (seq (remove (comp form/get-form-template :form/id) forms))]
+    {:success false
+     :errors [{:type :invalid-form
+               :forms invalid}]}))
 
 (defn invalid-users-error [userids]
-  (let [invalid (seq (remove users/user-exists? userids))]
-    (when invalid
-      {:success false
-       :errors [{:type :invalid-user
-                 :users invalid}]})))
+  (when-some [invalid (seq (remove users/user-exists? userids))]
+    {:success false
+     :errors [{:type :invalid-user
+               :users invalid}]}))
 
-(defn create-workflow! [{:keys [organization handlers forms] :as cmd}]
+(defn invalid-licenses-error [licenses]
+  (when-some [invalid (seq (remove (comp licenses/license-exists? :license/id) licenses))]
+    {:success false
+     :errors [{:type :invalid-license
+               :licenses invalid}]}))
+
+(defn create-workflow! [{:keys [organization handlers licenses forms] :as cmd}]
   (util/check-allowed-organization! organization)
   (or (invalid-users-error handlers)
       (invalid-forms-error forms)
-      (let [id (workflow/create-workflow! cmd)]
+      (invalid-licenses-error licenses)
+      (let [id (workflow/create-workflow! (update cmd :licenses #(map :license/id %)))]
         (dependencies/reset-cache!)
         {:success (not (nil? id))
          :id id})))
 
-(defn- unrich-workflow [workflow]
-  ;; TODO: keep handlers always in the same format, to avoid this conversion (we can ignore extra keys)
-  (if (get-in workflow [:workflow :handlers])
-    (update-in workflow [:workflow :handlers] #(map :userid %))
-    workflow))
-
-;; TODO: use rems.db.workflow/edit-workflow! and don't go directly to db fns, same for other fns
-(defn edit-workflow! [{:keys [id organization title handlers]}]
-  (let [workflow (unrich-workflow (workflow/get-workflow id))
-        workflow-body (cond-> (:workflow workflow)
-                        handlers (assoc :handlers handlers))]
+(defn edit-workflow! [{:keys [id organization handlers] :as cmd}]
+  (let [workflow (workflow/get-workflow id)]
     (util/check-allowed-organization! (:organization workflow))
     (when organization
       (util/check-allowed-organization! organization))
-    (db/edit-workflow! {:id id
-                        :title title
-                        :organization (:organization/id organization)
-                        :workflow (json/generate-string workflow-body)}))
-  (applications/reload-cache!)
-  {:success true})
+    (or (invalid-users-error handlers)
+        (do
+          (workflow/edit-workflow! (update cmd :licenses #(map :license/id %)))
+          (applications/reload-cache!)
+          {:success true}))))
 
 (defn set-workflow-enabled! [{:keys [id enabled]}]
   (util/check-allowed-organization! (:organization (workflow/get-workflow id)))
@@ -69,18 +63,21 @@
 
 ;; TODO more systematic joining for these needed. Now we just add the title for the UI
 (defn- enrich-workflow-form [item]
-  (select-keys (dependencies/enrich-dependency item) [:form/id :form/internal-name :form/external-title]))
+  (-> item
+      dependencies/enrich-dependency
+      (select-keys [:form/id :form/internal-name :form/external-title])))
 
-(defn- join-workflow-forms [workflow]
-  (update-in workflow [:workflow :forms] (partial mapv enrich-workflow-form)))
+(defn- enrich-workflow-license [item]
+  (-> item
+      licenses/join-license
+      organizations/join-organization))
 
 (defn- join-dependencies [workflow]
   (when workflow
-    (->> workflow
-         join-workflow-forms
-         organizations/join-organization
-         workflow/join-workflow-licenses
-         (transform [:licenses ALL] organizations/join-organization))))
+    (-> workflow
+        organizations/join-organization
+        (update-in [:workflow :forms] (partial map enrich-workflow-form))
+        (update-in [:workflow :licenses] (partial map enrich-workflow-license)))))
 
 (defn get-workflow [id]
   (->> (workflow/get-workflow id)
@@ -99,3 +96,4 @@
                            (get-in wf [:workflow :handlers]))
                          workflows)]
     (->> handlers distinct (sort-by :userid))))
+
diff --git a/src/clj/rems/api/workflows.clj b/src/clj/rems/api/workflows.clj
index 13a92d1c7..06375792e 100644
--- a/src/clj/rems/api/workflows.clj
+++ b/src/clj/rems/api/workflows.clj
@@ -6,7 +6,6 @@
             [rems.application.events :as events]
             [rems.common.roles :refer [+admin-read-roles+ +admin-write-roles+]]
             [rems.schema-base :as schema-base]
-            [rems.util :refer [getx-user-id]]
             [ring.util.http-response :refer :all]
             [schema.core :as s]))
 
@@ -15,7 +14,8 @@
    :title s/Str
    (s/optional-key :forms) [{:form/id s/Int}]
    :type (apply s/enum events/workflow-types) ; TODO: exclude master workflow?
-   (s/optional-key :handlers) [schema-base/UserId]})
+   (s/optional-key :handlers) [schema-base/UserId]
+   (s/optional-key :licenses) [schema-base/LicenseId]})
 
 (s/defschema EditWorkflowCommand
   {:id s/Int
diff --git a/src/clj/rems/application/commands.clj b/src/clj/rems/application/commands.clj
index 892bbeaa8..208ca8c12 100644
--- a/src/clj/rems/application/commands.clj
+++ b/src/clj/rems/application/commands.clj
@@ -322,9 +322,8 @@
 (defn- build-licenses-list [catalogue-item-ids {:keys [get-catalogue-item-licenses]}]
   (->> catalogue-item-ids
        (mapcat get-catalogue-item-licenses)
-       distinct
-       (mapv (fn [license]
-               {:license/id (:id license)}))))
+       (distinct-by :license/id)
+       (mapv #(select-keys % [:license/id]))))
 
 (defn- entitlement-end-not-in-future-error
   "Checks that entitlement date time, if provided, is in the future."
diff --git a/src/clj/rems/application/model.clj b/src/clj/rems/application/model.clj
index 8e06e4ac2..442d0902d 100644
--- a/src/clj/rems/application/model.clj
+++ b/src/clj/rems/application/model.clj
@@ -2,7 +2,7 @@
   (:require [clojure.set :as set]
             [clojure.test :refer [deftest is testing]]
             [com.rpl.specter :refer [ALL select transform]]
-            [medley.core :refer [find-first map-vals update-existing update-existing-in]]
+            [medley.core :refer [distinct-by find-first map-vals update-existing update-existing-in]]
             [rems.application.events :as events]
             [rems.application.master-workflow :as master-workflow]
             [rems.common.application-util :as application-util]
@@ -507,6 +507,16 @@
       (-> application
           (assoc-in [:application/duo :duo/matches] matches)))))
 
+(defn- enrich-workflow-licenses [application get-workflow]
+  (let [wf (get-workflow (get-in application [:application/workflow :workflow/id]))]
+    (if-some [workflow-licenses (seq (get-in wf [:workflow :licenses]))]
+      (-> application
+          (update :application/licenses (fn [app-licenses]
+                                          (->> workflow-licenses
+                                               (into app-licenses)
+                                               (distinct-by :license/id)))))
+      application)))
+
 (defn- enrich-licenses [app-licenses get-license]
   (let [rich-licenses (->> app-licenses
                            (map :license/id)
@@ -676,6 +686,7 @@
       (update :application/resources enrich-resources get-catalogue-item)
       enrich-application-duo-matches ; uses enriched resources
       (update-existing-in [:application/duo :duo/codes] duo/enrich-duo-codes)
+      (enrich-workflow-licenses get-workflow)
       (update :application/licenses enrich-licenses get-license)
       (update :application/events (partial mapv #(enrich-event % get-user get-catalogue-item)))
       (assoc :application/applicant (get-user (get-in application [:application/applicant :userid])))
diff --git a/src/clj/rems/db/applications.clj b/src/clj/rems/db/applications.clj
index 2080ae5e4..27c5c9f8b 100644
--- a/src/clj/rems/db/applications.clj
+++ b/src/clj/rems/db/applications.clj
@@ -6,12 +6,12 @@
             [clojure.test :refer [deftest is]]
             [clojure.tools.logging :as log]
             [conman.core :as conman]
-            [medley.core :refer [map-vals]]
+            [medley.core :refer [distinct-by map-vals]]
             [mount.core :as mount]
             [rems.application.events-cache :as events-cache]
             [rems.application.model :as model]
             [rems.auth.util :refer [throw-forbidden]]
-            [rems.common.util :refer [conj-set]]
+            [rems.common.util :refer [conj-set keep-keys]]
             [rems.config :refer [env]]
             [rems.db.attachments :as attachments]
             [rems.db.blacklist :as blacklist]
@@ -53,9 +53,13 @@
 ;;; Running commands
 
 (defn get-catalogue-item-licenses [catalogue-item-id]
-  (db/get-licenses
-   {:wfid (:wfid (catalogue/get-localized-catalogue-item catalogue-item-id {}))
-    :items [catalogue-item-id]}))
+  (let [item (catalogue/get-localized-catalogue-item catalogue-item-id {})
+        workflow-licenses (-> (workflow/get-workflow (:wfid item))
+                              (get-in [:workflow :licenses]))]
+    (->> (licenses/get-licenses {:items [catalogue-item-id]})
+         (keep-keys {:id :license/id})
+         (into workflow-licenses)
+         (distinct-by :license/id))))
 
 (defn get-application-by-invitation-token [invitation-token]
   (:id (db/get-application-by-invitation-token {:token invitation-token})))
diff --git a/src/clj/rems/db/licenses.clj b/src/clj/rems/db/licenses.clj
index 940ce4eaf..072f4d806 100644
--- a/src/clj/rems/db/licenses.clj
+++ b/src/clj/rems/db/licenses.clj
@@ -1,6 +1,6 @@
 (ns rems.db.licenses
   "querying localized licenses"
-  (:require [rems.common.util :refer [distinct-by]]
+  (:require [medley.core :refer [distinct-by]]
             [rems.db.core :as db]))
 
 (defn- format-license [license]
@@ -55,7 +55,6 @@
 
 (defn get-licenses
   "Get licenses. Params map can contain:
-     :wfid -- workflow to get workflow licenses for
      :items -- sequence of catalogue items to get resource licenses for"
   [params]
   (->> (db/get-licenses params)
@@ -67,7 +66,13 @@
   (assoc x :licenses (get-resource-licenses (:id x))))
 
 (defn join-catalogue-item-licenses [item]
-  (assoc item
-         :licenses
-         (get-licenses {:wfid (:wfid item)
-                        :items [(:id item)]})))
+  (assoc item :licenses (get-licenses {:items [(:id item)]})))
+
+(defn join-license [{:keys [license/id] :as x}]
+  (-> (get-license id)
+      (dissoc :id)
+      (merge x)))
+
+(defn license-exists? [id]
+  (some? (db/get-license {:id id})))
+
diff --git a/src/clj/rems/db/workflow.clj b/src/clj/rems/db/workflow.clj
index fc6a16b32..e82eaf637 100644
--- a/src/clj/rems/db/workflow.clj
+++ b/src/clj/rems/db/workflow.clj
@@ -1,17 +1,17 @@
 (ns rems.db.workflow
   (:require [rems.application.events :as events]
             [rems.db.core :as db]
-            [rems.db.licenses :as licenses]
             [rems.db.users :as users]
             [rems.json :as json]
-            [rems.util :refer [getx]]
             [schema.coerce :as coerce]
-            [schema.core :as s]))
+            [schema.core :as s]
+            [medley.core :refer [update-existing-in]]))
 
 (s/defschema WorkflowBody
   {:type (apply s/enum events/workflow-types)
    :handlers [s/Str]
-   (s/optional-key :forms) [{:form/id s/Num}]})
+   (s/optional-key :forms) [{:form/id s/Num}]
+   (s/optional-key :licenses) [s/Int]})
 
 (def ^:private coerce-workflow-body
   (coerce/coercer! WorkflowBody coerce/string-coercion-matcher))
@@ -19,24 +19,21 @@
 (def ^:private validate-workflow-body
   (s/validator WorkflowBody))
 
-(defn create-workflow! [{:keys [organization type title handlers forms]}]
+(defn create-workflow! [{:keys [organization type title handlers forms licenses]}]
   (let [body {:type type
               :handlers handlers
-              :forms forms}]
+              :forms forms
+              :licenses licenses}]
     (:id (db/create-workflow! {:organization (:organization/id organization)
                                :title title
                                :workflow (json/generate-string
                                           (validate-workflow-body body))}))))
 
-(defn- get-workflow-licenses [id]
-  (->> (db/get-workflow-licenses {:wfid id})
-       (mapv #(licenses/get-license (getx % :licid)))))
-
 (defn- enrich-and-format-workflow [wf]
   (-> wf
       (update :workflow #(coerce-workflow-body (json/parse-string %)))
       (update :organization (fn [id] {:organization/id id}))
-      (assoc :licenses (get-workflow-licenses (:id wf)))
+      (update-in [:workflow :licenses] #(mapv (fn [id] {:license/id id}) %))
       (update-in [:workflow :handlers] #(mapv users/get-user %))))
 
 (defn get-workflow [id]
@@ -48,20 +45,16 @@
        (map enrich-and-format-workflow)
        (db/apply-filters filters)))
 
-(defn join-workflow-licenses [workflow]
-  (assoc workflow :licenses (get-workflow-licenses (:id workflow))))
-
 (defn get-all-workflow-roles [userid]
   (when (some #(contains? (set (map :userid (get-in % [:workflow :handlers]))) userid)
               (get-workflows nil))
     #{:handler}))
 
 (defn- unrich-workflow [workflow]
-  ;; TODO: service does this too
   ;; TODO: keep handlers always in the same format, to avoid this conversion (we can ignore extra keys)
-  (if (get-in workflow [:workflow :handlers])
-    (update-in workflow [:workflow :handlers] #(map :userid %))
-    workflow))
+  (-> workflow
+      (update-existing-in [:workflow :handlers] #(map :userid %))
+      (update-existing-in [:workflow :licenses] #(map :license/id %))))
 
 (defn edit-workflow! [{:keys [id organization title handlers]}]
   (let [workflow (unrich-workflow (get-workflow id))
@@ -69,6 +62,7 @@
                         handlers (assoc :handlers handlers))]
     (db/edit-workflow! {:id (or id (:id workflow))
                         :title (or title (:title workflow))
-                        :organization (or (:organization/id organization) (get-in workflow [:organization :organization/id]))
+                        :organization (or (:organization/id organization)
+                                          (get-in workflow [:organization :organization/id]))
                         :workflow (json/generate-string workflow-body)}))
   {:success true})
diff --git a/src/clj/rems/middleware.clj b/src/clj/rems/middleware.clj
index c6744ff6f..7dc6cac55 100644
--- a/src/clj/rems/middleware.clj
+++ b/src/clj/rems/middleware.clj
@@ -14,8 +14,7 @@
             [rems.db.organizations :as organizations]
             [rems.db.roles :as roles]
             [rems.db.user-settings :as user-settings]
-            [rems.db.users :as users]
-            [rems.db.workflow :as workflows]
+            [rems.db.workflow :as workflow]
             [rems.layout :refer [error-page]]
             [rems.logging :refer [with-mdc]]
             [rems.middleware.dev :refer [wrap-dev]]
@@ -71,7 +70,7 @@
                                (when context/*user*
                                  (set/union (roles/get-roles (getx-user-id))
                                             (organizations/get-all-organization-roles (getx-user-id))
-                                            (workflows/get-all-workflow-roles (getx-user-id))
+                                            (workflow/get-all-workflow-roles (getx-user-id))
                                             (applications/get-all-application-roles (getx-user-id))))
                                (when (:uses-valid-api-key? request)
                                  #{:api-key}))]
diff --git a/src/clj/rems/migrations/refactor_workflow_licenses.clj b/src/clj/rems/migrations/refactor_workflow_licenses.clj
new file mode 100644
index 000000000..54ecd6311
--- /dev/null
+++ b/src/clj/rems/migrations/refactor_workflow_licenses.clj
@@ -0,0 +1,55 @@
+(ns rems.migrations.refactor-workflow-licenses
+  (:require [clojure.test :refer [deftest is]]
+            [hugsql.core :as hugsql]
+            [rems.json :as json]
+            [rems.common.util :refer [build-index]]))
+
+(hugsql/def-db-fns-from-string
+  "
+-- :name get-workflow-licenses :*
+SELECT wfid, licid FROM workflow_licenses;
+   
+-- :name get-workflow :? :1
+SELECT id, workflowbody::TEXT
+FROM workflow
+WHERE id = :id;
+   
+-- :name set-workflow-body! :!
+UPDATE workflow
+SET workflowBody = :workflowbody::jsonb
+WHERE id = :id;
+")
+
+(defn migrate-workflow-licenses [workflow licenses]
+  (assoc-in workflow [:workflowbody :licenses] (sort licenses)))
+
+(defn migrate-workflows! [conn workflow-licenses]
+  (let [workflows (->> workflow-licenses
+                       (build-index {:keys [:wfid]
+                                     :value-fn :licid
+                                     :collect-fn set}))]
+    (doseq [[wfid licenses] workflows
+            :let [workflow (get-workflow conn {:id wfid})
+                  _ (assert workflow)
+                  workflow (update workflow :workflowbody json/parse-string)]]
+      (set-workflow-body! conn (update (migrate-workflow-licenses workflow licenses)
+                                       :workflowbody
+                                       json/generate-string)))))
+
+(deftest test-migrate-workflows!
+  (let [workflow-licenses [{:wfid 1 :licid 10}
+                           {:wfid 1 :licid 11}
+                           {:wfid 2 :licid 12}]
+        workflows (atom [])]
+    (with-redefs [get-workflow (fn [_ {:keys [id]}]
+                                 {:id id
+                                  :workflowbody (json/generate-string {})})
+                  set-workflow-body! (fn [_ wf] (swap! workflows conj wf))]
+      (migrate-workflows! nil workflow-licenses)
+      (is (= [{:id 1 :workflowbody {:licenses [10 11]}}
+              {:id 2 :workflowbody {:licenses [12]}}]
+             (->> @workflows
+                  (mapv #(update % :workflowbody json/parse-string))))))))
+
+(defn migrate-up [{:keys [conn]}]
+  (migrate-workflows! conn (get-workflow-licenses conn)))
diff --git a/src/clj/rems/schema_base.clj b/src/clj/rems/schema_base.clj
index b1be7eda9..cc27cb639 100644
--- a/src/clj/rems/schema_base.clj
+++ b/src/clj/rems/schema_base.clj
@@ -114,3 +114,6 @@
   (merge Category
          {(s/optional-key :category/children) [Category]}))
 
+(s/defschema LicenseId
+  {:license/id s/Int})
+
diff --git a/src/cljc/rems/common/util.cljc b/src/cljc/rems/common/util.cljc
index 5968ab472..8598f3d2b 100644
--- a/src/cljc/rems/common/util.cljc
+++ b/src/cljc/rems/common/util.cljc
@@ -1,5 +1,5 @@
 (ns rems.common.util
-  (:require [medley.core :refer [map-vals]]
+  (:require [medley.core :refer [map-keys map-vals remove-keys]]
             [clojure.set :as set]
             [clojure.string :as str]
             [clojure.test :refer [deftest is testing]]))
@@ -382,14 +382,6 @@
                         {:id 4 :unrelated "is destroyed in a cycle"}
                         {:id 5 :unrelated "survives"}])))))
 
-(defn distinct-by
-  "Remove duplicates from sequence, comparing the value returned by key-fn.
-   The first element that key-fn returns a given value for is retained.
-
-   Order of sequence is not preserved in any way."
-  [key-fn sequence]
-  (map first (vals (group-by key-fn sequence))))
-
 (defn andstr
   "Like `apply str coll` but only produces something if all the
   values are truthy like with `and`.
@@ -545,7 +537,10 @@
   (is (= "src/foo/bar.clj" (normalize-file-path "/home/john/rems/src/foo/bar.clj")))
   (is (= "src/foo/bar.clj" (normalize-file-path "C:\\Users\\john\\rems\\src\\foo/bar.clj"))))
 
-(defn assoc-some-in [m ks v]
+(defn assoc-some-in
+  "Like `clojure.core/assoc-in`, but only associates value `v` in key path
+   `ks` of associate collection `m` when `(true? (some? v)))`."
+  [m ks v]
   (if (some? v)
     (assoc-in m ks v)
     m))
@@ -594,7 +589,10 @@
   (is (= {:a 1 :b true} (update-present {:a 1 :b nil} :b (constantly true)))))
 
 ;; https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id
-(defn escape-element-id [id]
+(defn escape-element-id
+  "Replaces non-conforming characters from string `id` for the purpose
+   of creating an identifier suitable for HTML element id."
+  [id]
   (when (string? id)
     (-> id
         (str/replace #"[^A-Za-z0-9\-\_]" "_") ; "only ASCII letters, digits, '_', and '-' should be used,"
@@ -608,3 +606,17 @@
     (is (= "id_123-element" (escape-element-id "123-element")))
     (is (= "id__123-element" (escape-element-id " 123-element")))))
 
+(defn keep-keys
+  "Takes a sequence of associative collections and maps function `f`
+   over keys of each, returning a new associative collection with
+   non-nil keys. Useful for renaming and selecting only a subset of
+   associative collection keys."
+  [f coll]
+  (->> coll
+       (map (partial map-keys f))
+       (map (partial remove-keys nil?))))
+
+(deftest test-keep-keys
+  (is (= [] (keep-keys {} [])))
+  (is (= [{:b 1}] (keep-keys {:a :b} [{:a 1}])))
+  (is (= [{:b 1} {:c 2}] (keep-keys {:a :b :b :c} [{:a 1} {:b 2}]))))
diff --git a/src/cljs/rems/administration/create_workflow.cljs b/src/cljs/rems/administration/create_workflow.cljs
index f31b3b1e9..d8540a90d 100644
--- a/src/cljs/rems/administration/create_workflow.cljs
+++ b/src/cljs/rems/administration/create_workflow.cljs
@@ -7,28 +7,29 @@
             [rems.atoms :as atoms :refer [enrich-user document-title]]
             [rems.collapsible :as collapsible]
             [rems.config :as config]
+            [rems.common.util :refer [keep-keys replace-key]]
             [rems.dropdown :as dropdown]
             [rems.fetcher :as fetcher]
             [rems.fields :as fields]
             [rems.flash-message :as flash-message]
             [rems.spinner :as spinner]
-            [rems.text :refer [text]]
+            [rems.text :refer [localized text]]
             [rems.util :refer [navigate! post! put! trim-when-string]]))
 
 (defn- item-by-id [items id-key id]
   (find-first #(= (id-key %) id) items))
 
-(rf/reg-event-fx
- ::enter-page
- (fn [{:keys [db]} [_ workflow-id]]
-   {:db (assoc db
-               ::workflow-id workflow-id
-               ::actors nil
-               ::editing? (some? workflow-id)
-               ::form {:type :workflow/default})
-    :dispatch-n [[::actors]
-                 [::forms {:disabled true :archived true}]
-                 (when workflow-id [::workflow])]}))
+(rf/reg-event-fx ::enter-page
+                 (fn [{:keys [db]} [_ workflow-id]]
+                   {:db (assoc db
+                               ::workflow-id workflow-id
+                               ::actors nil
+                               ::editing? (some? workflow-id)
+                               ::form {:type :workflow/default})
+                    :dispatch-n [[::actors]
+                                 [::forms {:disabled true :archived true}]
+                                 [::licenses]
+                                 (when workflow-id [::workflow])]}))
 
 (rf/reg-sub ::workflow-id (fn [db _] (::workflow-id db)))
 (rf/reg-sub ::editing? (fn [db _] (::editing? db)))
@@ -37,16 +38,18 @@
 
 (rf/reg-sub ::form (fn [db _] (::form db)))
 
-(rf/reg-event-db ::set-form-field (fn [db [_ keys value]] (assoc-in db (concat [::form] keys) value)))
+(rf/reg-event-db ::set-form-field
+                 (fn [db [_ keys value]] (assoc-in db (concat [::form] keys) value)))
 
-(rf/reg-event-db
- ::fetch-workflow-success
- (fn [db [_ {:keys [title organization workflow]}]]
-   (update db ::form merge {:title title
-                            :organization organization
-                            :type (:type workflow)
-                            :forms (mapv #(select-keys % [:form/id]) (get workflow :forms))
-                            :handlers (get workflow :handlers)})))
+(rf/reg-event-db ::fetch-workflow-success
+                 (fn [db [_ {:keys [title organization workflow]}]]
+                   (update db ::form merge {:title title
+                                            :organization organization
+                                            :type (:type workflow)
+                                            :forms (mapv #(select-keys % [:form/id]) (get workflow :forms))
+                                            :handlers (get workflow :handlers)
+                                            :licenses (->> (:licenses workflow)
+                                                           (map #(replace-key % :license/id :id)))})))
 
 (fetcher/reg-fetcher ::workflow "/api/workflows/:id" {:path-params (fn [db] {:id (::workflow-id db)})
                                                       :on-success #(rf/dispatch [::fetch-workflow-success %])})
@@ -72,7 +75,8 @@
                  {:organization {:organization/id (get-in form [:organization :organization/id])}
                   :title (trim-when-string (:title form))
                   :type (:type form)
-                  :forms (mapv #(select-keys % [:form/id]) (:forms form))}
+                  :forms (mapv #(select-keys % [:form/id]) (:forms form))
+                  :licenses (vec (keep-keys {:id :license/id} (:licenses form)))}
                  (when (needs-handlers? (:type form))
                    {:handlers (map :userid (:handlers form))}))]
     (when (valid-create-request? request)
@@ -92,27 +96,25 @@
     (when (valid-edit-request? request)
       request)))
 
-(rf/reg-event-fx
- ::create-workflow
- (fn [_ [_ request]]
-   (let [description [text :t.administration/create-workflow]]
-     (post! "/api/workflows/create"
-            {:params request
-             :handler (flash-message/default-success-handler
-                       :top description #(navigate! (str "/administration/workflows/" (:id %))))
-             :error-handler (flash-message/default-error-handler :top description)}))
-   {}))
-
-(rf/reg-event-fx
- ::edit-workflow
- (fn [_ [_ request]]
-   (let [description [text :t.administration/edit-workflow]]
-     (put! "/api/workflows/edit"
-           {:params request
-            :handler (flash-message/default-success-handler
-                      :top description #(navigate! (str "/administration/workflows/" (:id request))))
-            :error-handler (flash-message/default-error-handler :top description)}))
-   {}))
+(rf/reg-event-fx ::create-workflow
+                 (fn [_ [_ request]]
+                   (let [description [text :t.administration/create-workflow]]
+                     (post! "/api/workflows/create"
+                            {:params request
+                             :handler (flash-message/default-success-handler
+                                       :top description #(navigate! (str "/administration/workflows/" (:id %))))
+                             :error-handler (flash-message/default-error-handler :top description)}))
+                   {}))
+
+(rf/reg-event-fx ::edit-workflow
+                 (fn [_ [_ request]]
+                   (let [description [text :t.administration/edit-workflow]]
+                     (put! "/api/workflows/edit"
+                           {:params request
+                            :handler (flash-message/default-success-handler
+                                      :top description #(navigate! (str "/administration/workflows/" (:id request))))
+                            :error-handler (flash-message/default-error-handler :top description)}))
+                   {}))
 
 (rf/reg-event-db ::set-handlers (fn [db [_ handlers]] (assoc-in db [::form :handlers] (sort-by :userid handlers))))
 
@@ -122,6 +124,12 @@
 
 (fetcher/reg-fetcher ::forms "/api/forms")
 
+(rf/reg-sub ::selected-licenses (fn [db _] (get-in db [::form :licenses])))
+(rf/reg-event-db ::set-licenses (fn [db [_ licenses]]
+                                  (->> (sort-by :id licenses)
+                                       (assoc-in db [::form :licenses]))))
+(fetcher/reg-fetcher ::licenses "/api/licenses")
+
 ;;;; UI
 
 (def ^:private context
@@ -137,6 +145,35 @@
   [text-field context {:keys [:title]
                        :label (text :t.create-workflow/title)}])
 
+(def ^:private licenses-dropdown-id "licenses-dropdown")
+
+(defn- workflow-licenses-field []
+  (let [licenses @(rf/subscribe [::licenses])
+        selected-licenses @(rf/subscribe [::selected-licenses])
+        editing? @(rf/subscribe [::editing?])]
+    [:div.form-group
+     [:label.administration-field-label {:for licenses-dropdown-id}
+      (text :t.create-resource/licenses-selection)]
+     (if editing?
+       [fields/readonly-field-raw
+        {:id "workflow-licenses"
+         :values (for [license selected-licenses
+                       :let [uri (str "/administration/licenses/" (:license/id license))
+                             title (:title (localized (:localizations license)))]]
+                   [atoms/link nil uri title])}]
+       [dropdown/dropdown
+        {:id licenses-dropdown-id
+         :items licenses
+         :item-key :id
+         :item-label (fn [license]
+                       (let [title (:title (localized (:localizations license)))
+                             organization (localized (get-in license [:organization
+                                                                      :organization/short-name]))]
+                         (str title " (org: " organization ")"))) ; XXX: workaround for get-localized-title
+         :item-selected? #(contains? (set selected-licenses) %)
+         :multi? true
+         :on-change #(rf/dispatch [::set-licenses %])}])]))
+
 (defn- workflow-type-field []
   [radio-button-group context {:id :workflow-type
                                :keys [:type]
@@ -256,13 +293,15 @@
                  [:div#workflow-editor.fields
                   [workflow-organization-field]
                   [workflow-title-field]
-                  [workflow-type-field]
 
+                  [workflow-type-field]
                   (case workflow-type
                     :workflow/default [default-workflow-form]
                     :workflow/decider [decider-workflow-form]
                     :workflow/master [master-workflow-form])
 
+                  [workflow-licenses-field]
+
                   [:div.col.commands
                    [cancel-button]
                    [save-workflow-button]]])}]]))
diff --git a/src/cljs/rems/administration/workflow.cljs b/src/cljs/rems/administration/workflow.cljs
index 8f3117007..ba0b379eb 100644
--- a/src/cljs/rems/administration/workflow.cljs
+++ b/src/cljs/rems/administration/workflow.cljs
@@ -3,7 +3,6 @@
             [re-frame.core :as rf]
             [rems.administration.administration :as administration]
             [rems.administration.components :refer [inline-info-field]]
-            [rems.administration.license :refer [licenses-view]]
             [rems.administration.status-flags :as status-flags]
             [rems.atoms :as atoms :refer [document-title enrich-user readonly-checkbox]]
             [rems.collapsible :as collapsible]
@@ -11,7 +10,7 @@
             [rems.flash-message :as flash-message]
             [rems.common.roles :as roles]
             [rems.spinner :as spinner]
-            [rems.text :refer [text]]
+            [rems.text :refer [localized text]]
             [rems.util :refer [fetch]]))
 
 (rf/reg-event-fx
@@ -64,11 +63,19 @@
                                                                          (str/join ", "))]
               [inline-info-field (text :t.administration/active) [readonly-checkbox {:value (status-flags/active? workflow)}]]
               [inline-info-field (text :t.administration/forms)
-               (into [:ul.list-group]
-                     (for [form (get-in workflow [:workflow :forms])]
-                       [:li.list-group-item [atoms/link nil (str "/administration/forms/" (:form/id form)) (:form/internal-name form)]]))
-               {:box? false}]]}]
-   [licenses-view (:licenses workflow) language]
+               (->> (for [form (get-in workflow [:workflow :forms])
+                          :let [uri (str "/administration/forms/" (:form/id form))
+                                title (:form/internal-name form)]]
+                      [atoms/link nil uri title])
+                    (interpose ", ")
+                    (into [:<>]))]
+              [inline-info-field (text :t.administration/licenses)
+               (->> (for [license (get-in workflow [:workflow :licenses])
+                          :let [uri (str "/administration/licenses/" (:license/id license))
+                                title (:title (localized (:localizations license)))]]
+                      [atoms/link nil uri title])
+                    (interpose ", ")
+                    (into [:<>]))]]}]
    (let [id (:id workflow)]
      [:div.col.commands
       [administration/back-button "/administration/workflows"]


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 046353a61..da9355b32 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -13,6 +13,7 @@
             [rems.db.users :as users]
             [rems.db.test-data-helpers :refer :all]
             [rems.db.test-data-users :refer :all]
+            [rems.db.workflow :as workflow]
             [rems.testing-util :refer [with-user]]
             [rems.config])
   (:import [java.util UUID]
@@ -234,75 +235,74 @@
         owner (users :owner)
         organization-owner1 (users :organization-owner1)
         handlers [approver1 approver2 rejecter-bot]
+        link (create-license! {:actor owner
+                               :license/type :link
+                               :organization {:organization/id "nbn"}
+                               :license/title {:en "CC Attribution 4.0"
+                                               :fi "CC Nimeä 4.0"
+                                               :sv "CC Erkännande 4.0"}
+                               :license/link {:en "https://creativecommons.org/licenses/by/4.0/legalcode"
+                                              :fi "https://creativecommons.org/licenses/by/4.0/legalcode.fi"
+                                              :sv "https://creativecommons.org/licenses/by/4.0/legalcode.sv"}})
+        text (create-license! {:actor owner
+                               :license/type :text
+                               :organization {:organization/id "nbn"}
+                               :license/title {:en "General Terms of Use"
+                                               :fi "Yleiset käyttöehdot"
+                                               :sv "Allmänna villkor"}
+                               :license/text {:en (apply str (repeat 10 "License text in English. "))
+                                              :fi (apply str (repeat 10 "Suomenkielinen lisenssiteksti. "))
+                                              :sv (apply str (repeat 10 "Licens på svenska. "))}})
         default (create-workflow! {:actor owner
                                    :organization {:organization/id "nbn"}
                                    :title "Default workflow"
                                    :type :workflow/default
-                                   :handlers handlers})
+                                   :handlers handlers
+                                   :licenses [link text]})
         decider (create-workflow! {:actor owner
                                    :organization {:organization/id "nbn"}
                                    :title "Decider workflow"
                                    :type :workflow/decider
-                                   :handlers handlers})
+                                   :handlers handlers
+                                   :licenses [link text]})
         master (create-workflow! {:actor owner
                                   :organization {:organization/id "nbn"}
                                   :title "Master workflow"
                                   :type :workflow/master
-                                  :handlers handlers})
+                                  :handlers handlers
+                                  :licenses [link text]})
         auto-approve (create-workflow! {:actor owner
                                         :organization {:organization/id "nbn"}
                                         :title "Auto-approve workflow"
-                                        :handlers [approver-bot rejecter-bot]})
+                                        :handlers [approver-bot rejecter-bot]
+                                        :licenses [link text]})
         organization-owner (create-workflow! {:actor organization-owner1
                                               :organization {:organization/id "organization1"}
                                               :title "Owned by organization owner"
                                               :type :workflow/default
                                               :handlers handlers})
-        with-form (create-workflow! {:actor owner
-                                     :organization {:organization/id "nbn"}
-                                     :title "With workflow form"
-                                     :type :workflow/default
-                                     :handlers handlers
-                                     :forms [{:form/id (create-form! {:actor owner
-                                                                      :form/internal-name "Workflow form"
-                                                                      :form/external-title {:en "Workflow form"
-                                                                                            :fi "Työvuon lomake"
-                                                                                            :sv "Blankett för arbetsflöde"}
-                                                                      :organization {:organization/id "nbn"}
-                                                                      :form/fields [{:field/type :description
-                                                                                     :field/title {:fi "Kuvaus"
-                                                                                                   :en "Description"
-                                                                                                   :sv "Rubrik"}
-                                                                                     :field/optional false}]})}]})
+        _with-form (create-workflow! {:actor owner
+                                      :organization {:organization/id "nbn"}
+                                      :title "With workflow form"
+                                      :type :workflow/default
+                                      :handlers handlers
+                                      :licenses [link text]
+                                      :forms [{:form/id (create-form! {:actor owner
+                                                                       :form/internal-name "Workflow form"
+                                                                       :form/external-title {:en "Workflow form"
+                                                                                             :fi "Työvuon lomake"
+                                                                                             :sv "Blankett för arbetsflöde"}
+                                                                       :organization {:organization/id "nbn"}
+                                                                       :form/fields [{:field/type :description
+                                                                                      :field/title {:fi "Kuvaus"
+                                                                                                    :en "Description"
+                                                                                                    :sv "Rubrik"}
+                                                                                      :field/optional false}]})}]})
         ega (create-workflow! {:actor owner
                                :organization {:organization/id "csc"}
                                :title "EGA workflow, a variant of default"
                                :type :workflow/default
                                :handlers handlers})]
-
-    ;; attach both kinds of licenses to all workflows created by owner except EGA
-    (let [link (create-license! {:actor owner
-                                 :license/type :link
-                                 :organization {:organization/id "nbn"}
-                                 :license/title {:en "CC Attribution 4.0"
-                                                 :fi "CC Nimeä 4.0"
-                                                 :sv "CC Erkännande 4.0"}
-                                 :license/link {:en "https://creativecommons.org/licenses/by/4.0/legalcode"
-                                                :fi "https://creativecommons.org/licenses/by/4.0/legalcode.fi"
-                                                :sv "https://creativecommons.org/licenses/by/4.0/legalcode.sv"}})
-          text (create-license! {:actor owner
-                                 :license/type :text
-                                 :organization {:organization/id "nbn"}
-                                 :license/title {:en "General Terms of Use"
-                                                 :fi "Yleiset käyttöehdot"
-                                                 :sv "Allmänna villkor"}
-                                 :license/text {:en (apply str (repeat 10 "License text in English. "))
-                                                :fi (apply str (repeat 10 "Suomenkielinen lisenssiteksti. "))
-                                                :sv (apply str (repeat 10 "Licens på svenska. "))}})]
-      (doseq [licid [link text]]
-        (doseq [wfid [default decider master auto-approve with-form]]
-          (db/create-workflow-license! {:wfid wfid :licid licid}))))
-
     {:default default
      :ega ega
      :decider decider
@@ -744,8 +744,8 @@
                                                                         :more-info {:en "This DUO code is optional but recommended"}}]}}))
 
         workflows (create-workflows! (merge users +bot-users+))
-        _ (db/create-workflow-license! {:wfid (:organization-owner workflows)
-                                        :licid license-organization-owner})
+        _ (workflow/edit-workflow! {:id (:organization-owner workflows)
+                                    :licenses [license-organization-owner]})
 
         form (create-all-field-types-example-form! owner {:organization/id "nbn"} "Example form with all field types" {:en "Example form with all field types"
                                                                                                                        :fi "Esimerkkilomake kaikin kenttätyypein"
diff --git a/src/clj/rems/db/test_data_helpers.clj b/src/clj/rems/db/test_data_helpers.clj
index 92c185e39..3cdbd28d3 100644
--- a/src/clj/rems/db/test_data_helpers.clj
+++ b/src/clj/rems/db/test_data_helpers.clj
@@ -116,9 +116,6 @@
                         :license/text {:fi "fi" :en "en"}
                         :license/attachment-id {:fi fi-attachment :en en-attachment}}))))
 
-(defn create-workflow-licence! [wfid licid]
-  (db/create-workflow-license! {:wfid wfid :licid licid}))
-
 (defn create-form! [{:keys [actor organization]
                      :form/keys [internal-name external-title fields]
                      :as command}]
@@ -146,7 +143,7 @@
     (assert (:success result) {:command command :result result})
     (:id result)))
 
-(defn create-workflow! [{:keys [actor organization title type handlers forms]
+(defn create-workflow! [{:keys [actor organization title type handlers forms licenses]
                          :as command}]
   (let [actor (or actor (create-owner!))
         result (with-user actor
@@ -157,7 +154,8 @@
                    :forms forms
                    :handlers (or handlers
                                  (do (create-user! (get +fake-user-data+ "developer"))
-                                     ["developer"]))}))]
+                                     ["developer"]))
+                   :licenses (mapv (fn [id] {:license/id id}) licenses)}))]
     (assert (:success result) {:command command :result result})
     (:id result)))
 
diff --git a/test/clj/rems/api/services/test_dependencies.clj b/test/clj/rems/api/services/test_dependencies.clj
index 628037d57..69dc5ba77 100644
--- a/test/clj/rems/api/services/test_dependencies.clj
+++ b/test/clj/rems/api/services/test_dependencies.clj
@@ -25,7 +25,9 @@
         cat-form (test-helpers/create-form! {})
         unused-form (test-helpers/create-form! {})
         wf-1 (test-helpers/create-workflow! {:forms [{:form/id shared-form}]})
-        wf-2 (test-helpers/create-workflow! {:forms [{:form/id wf-form} {:form/id shared-form}]})
+        wf-2 (test-helpers/create-workflow! {:forms [{:form/id wf-form}
+                                                     {:form/id shared-form}]
+                                             :licenses [shared-license]})
         category-1 (test-helpers/create-category! {:category/title {:fi "Testikategoria"
                                                                     :en "Test category"
                                                                     :sv "Test kategori"}
@@ -43,8 +45,6 @@
         cat-2 (test-helpers/create-catalogue-item! {:resource-id res-1
                                                     :form-id shared-form
                                                     :workflow-id wf-2})]
-    ;; TODO no public way to set workflow licenses for now
-    (db/create-workflow-license! {:wfid wf-2 :licid shared-license})
 
     (testing "dependencies"
       (is (= {:dependencies
diff --git a/test/clj/rems/api/services/test_workflow.clj b/test/clj/rems/api/services/test_workflow.clj
index d2889c6b4..7fdd83a61 100644
--- a/test/clj/rems/api/services/test_workflow.clj
+++ b/test/clj/rems/api/services/test_workflow.clj
@@ -46,8 +46,8 @@
                                     :form/internal-name "workflow form"
                                     :form/external-title {:en "Workflow Form EN"
                                                           :fi "Workflow Form FI"
-                                                          :sv "Workflow Form SV"}}]}
-                :licenses []
+                                                          :sv "Workflow Form SV"}}]
+                           :licenses []}
                 :enabled true
                 :archived false}
                (workflow/get-workflow wf-id)))))
@@ -63,8 +63,8 @@
                 :workflow {:type :workflow/decider
                            :handlers [{:userid "user1" :name "User 1" :email "user1@example.com"}
                                       {:userid "user2" :name "User 2" :email "user2@example.com"}]
-                           :forms []}
-                :licenses []
+                           :forms []
+                           :licenses []}
                 :enabled true
                 :archived false}
                (workflow/get-workflow wf-id)))))
@@ -80,8 +80,8 @@
                 :workflow {:type :workflow/master
                            :handlers [{:userid "user1" :name "User 1" :email "user1@example.com"}
                                       {:userid "user2" :name "User 2" :email "user2@example.com"}]
-                           :forms []}
-                :licenses []
+                           :forms []
+                           :licenses []}
                 :enabled true
                 :archived false}
                (workflow/get-workflow wf-id)))))))
@@ -92,19 +92,24 @@
   (with-user "owner"
     (test-helpers/create-organization! {:organization/id "abc" :organization/name {:en "ABC"} :organization/short-name {:en "ABC"}})
     (testing "change title"
-      (let [wf-id (test-helpers/create-workflow! {:organization {:organization/id "abc"}
+      (let [licid (test-helpers/create-license! {:organization {:organization/id "abc"}})
+            wf-id (test-helpers/create-workflow! {:organization {:organization/id "abc"}
                                                   :type :workflow/master
                                                   :title "original title"
-                                                  :handlers ["user1"]})]
+                                                  :handlers ["user1"]
+                                                  :licenses [licid]})]
         (workflow/edit-workflow! {:id wf-id
                                   :title "changed title"})
         (is (= {:id wf-id
                 :title "changed title"
                 :workflow {:type :workflow/master
                            :handlers [{:userid "user1" :name "User 1" :email "user1@example.com"}]
-                           :forms []}}
+                           :forms []
+                           :licenses [{:license/id licid}]}}
                (-> (workflow/get-workflow wf-id)
-                   (select-keys [:id :title :workflow]))))))
+                   (select-keys [:id :title :workflow])
+                   (update-in [:workflow :licenses]
+                              (partial map #(select-keys % [:license/id]))))))))
 
     (testing "change handlers"
       (let [wf-id (test-helpers/create-workflow! {:organization {:organization/id "abc"}
@@ -117,7 +122,8 @@
                 :title "original title"
                 :workflow {:type :workflow/master
                            :handlers [{:userid "user2" :name "User 2" :email "user2@example.com"}]
-                           :forms []}}
+                           :forms []
+                           :licenses []}}
                (-> (workflow/get-workflow wf-id)
                    (select-keys [:id :title :workflow]))))))))
 
diff --git a/test/clj/rems/api/test_administration.clj b/test/clj/rems/api/test_administration.clj
index 5c64e4f07..37b993040 100644
--- a/test/clj/rems/api/test_administration.clj
+++ b/test/clj/rems/api/test_administration.clj
@@ -3,6 +3,7 @@
   (:require [clojure.test :refer :all]
             [rems.api.testing :refer :all]
             [rems.db.core :as db]
+            [rems.db.workflow :as workflow]
             [rems.db.testing :refer [owners-fixture +test-api-key+]]))
 
 (use-fixtures
@@ -32,7 +33,9 @@
         workflow-id (:id (api-call :post "/api/workflows/create"
                                    {:organization {:organization/id "organization2"} :title "default workflow"
                                     :forms [{:form/id wf-form-id}]
-                                    :type :workflow/default :handlers ["owner"]}
+                                    :type :workflow/default
+                                    :handlers ["owner"]
+                                    :licenses [{:license/id license-id}]}
                                    +test-api-key+ "owner"))
         catalogue-id (:id (api-call :post "/api/catalogue-items/create"
                                     {:form form-id
@@ -97,9 +100,6 @@
     (is workflow-id)
     (is catalogue-id)
 
-    ;; no api for this yet
-    (db/create-workflow-license! {:wfid workflow-id :licid license-id})
-
     (testing "can disable a resource"
       (is (:success (resource-enabled! false))))
 
diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj
index 3d1fcdcf3..29ca3806e 100644
--- a/test/clj/rems/api/test_applications.clj
+++ b/test/clj/rems/api/test_applications.clj
@@ -175,9 +175,11 @@
         license-id2 (test-helpers/create-license! {})
         license-id3 (test-helpers/create-license! {})
         license-id4 (test-helpers/create-license! {})
+        workflow-license-id (test-helpers/create-license! {})
         form-id (test-helpers/create-form! {})
         workflow-id (test-helpers/create-workflow! {:type :workflow/master
-                                                    :handlers [handler-id]})
+                                                    :handlers [handler-id]
+                                                    :licenses [workflow-license-id]})
         cat-item-id1 (test-helpers/create-catalogue-item! {:resource-id (test-helpers/create-resource!
                                                                          {:license-ids [license-id1 license-id2]})
                                                            :form-id form-id
@@ -198,7 +200,9 @@
       (is (= {:success true} (send-command user-id
                                            {:type :application.command/accept-licenses
                                             :application-id application-id
-                                            :accepted-licenses [license-id1 license-id2]})))
+                                            :accepted-licenses [license-id1
+                                                                license-id2
+                                                                workflow-license-id]})))
       (testing "with invalid application id"
         (is (= {:success false
                 :errors [{:type "application-not-found"}]}
@@ -398,14 +402,16 @@
       (testing "adding and then accepting additional licenses"
         (testing "add licenses"
           (let [application (get-application-for-user application-id user-id)]
-            (is (= #{license-id1 license-id2} (license-ids-for-application application)))
+            (is (= #{license-id1 license-id2 workflow-license-id}
+                   (license-ids-for-application application)))
             (is (= {:success true} (send-command handler-id
                                                  {:type :application.command/add-licenses
                                                   :application-id application-id
                                                   :licenses [license-id4]
                                                   :comment "Please approve these new terms"})))
             (let [application (get-application-for-user application-id user-id)]
-              (is (= #{license-id1 license-id2 license-id4} (license-ids-for-application application))))))
+              (is (= #{license-id1 license-id2 license-id4 workflow-license-id}
+                     (license-ids-for-application application))))))
         (testing "applicant accepts the additional licenses"
           (is (= {:success true} (send-command user-id
                                                {:type :application.command/accept-licenses
@@ -415,7 +421,8 @@
       (testing "changing resources as handler"
         (let [application (get-application-for-user application-id user-id)]
           (is (= #{cat-item-id2} (catalogue-item-ids-for-application application)))
-          (is (= #{license-id1 license-id2 license-id4} (license-ids-for-application application)))
+          (is (= #{license-id1 license-id2 license-id4 workflow-license-id}
+                 (license-ids-for-application application)))
           (is (= {:success true} (send-command handler-id
                                                {:type :application.command/change-resources
                                                 :application-id application-id
@@ -424,7 +431,8 @@
           (let [application (get-application-for-user application-id user-id)]
             (is (= #{cat-item-id3} (catalogue-item-ids-for-application application)))
             ;; TODO: The previously added licenses should probably be retained in the licenses after changing resources.
-            (is (= #{license-id3} (license-ids-for-application application))))))
+            (is (= #{license-id3 workflow-license-id}
+                   (license-ids-for-application application))))))
 
       (testing "changing resources back as handler"
         (is (= {:success true} (send-command handler-id
@@ -433,7 +441,8 @@
                                               :catalogue-item-ids [cat-item-id2]})))
         (let [application (get-application-for-user application-id user-id)]
           (is (= #{cat-item-id2} (catalogue-item-ids-for-application application)))
-          (is (= #{license-id1 license-id2} (license-ids-for-application application)))))
+          (is (= #{license-id1 license-id2 workflow-license-id}
+                 (license-ids-for-application application)))))
 
       (testing "request-decision with alternate id"
         (is (= {:success true} (send-command handler-id
diff --git a/test/clj/rems/api/test_workflows.clj b/test/clj/rems/api/test_workflows.clj
index af93a0dac..bb45452ed 100644
--- a/test/clj/rems/api/test_workflows.clj
+++ b/test/clj/rems/api/test_workflows.clj
@@ -1,13 +1,15 @@
 (ns ^:integration rems.api.test-workflows
   (:require [clojure.test :refer :all]
             [rems.api.services.licenses :as licenses]
-            [rems.api.services.workflow :as workflow]
+            [rems.api.services.workflow :as workflows]
             [rems.api.testing :refer :all]
+            [rems.common.util :refer [replace-key]]
             [rems.db.applications :as applications]
             [rems.db.core :as db]
             [rems.db.test-data-users :refer [+fake-user-data+]]
             [rems.db.test-data-helpers :as test-helpers]
             [rems.db.testing :refer [owners-fixture +test-api-key+]]
+            [rems.db.workflow :as workflow]
             [rems.handler :refer [handler]]
             [rems.testing-util :refer [with-user]]
             [ring.mock.request :refer :all]))
@@ -25,7 +27,8 @@
    :workflow {:type "workflow/default"
               :forms []
               :handlers [{:userid "handler" :email "handler@example.com" :name "Hannah Handler"}
-                         {:userid "carl" :email "carl@example.com" :name "Carl Reviewer"}]}
+                         {:userid "carl" :email "carl@example.com" :name "Carl Reviewer"}]
+              :licenses []}
    :enabled true
    :archived false})
 
@@ -126,12 +129,14 @@
 (deftest workflows-enabled-archived-test
   (create-handlers!)
   (let [user-id "owner"
+        lic-id (test-helpers/create-license! {:organization {:organization/id "organization1"}})
+        lic (-> (licenses/get-license lic-id)
+                (replace-key :id :license/id))
         wfid (test-helpers/create-workflow! {:organization {:organization/id "organization1"}
                                              :title "workflow title"
                                              :type :workflow/default
-                                             :handlers ["handler" "carl"]})
-        lic-id (test-helpers/create-license! {:organization {:organization/id "organization1"}})
-        _ (db/create-workflow-license! {:wfid wfid :licid lic-id :organization "organization1"})
+                                             :handlers ["handler" "carl"]
+                                             :licenses [lic-id]})
 
         fetch #(fetch +test-api-key+ user-id wfid)
         archive-license! #(with-user user-id
@@ -146,23 +151,28 @@
                                   :archived %1}
                                  +test-api-key+ %2)]
     (testing "before changes"
-      (is (= expected (fetch))))
+      (is (= (-> expected
+                 (assoc-in [:workflow :licenses] [lic]))
+             (fetch))))
     (testing "as owner"
       (testing "disable and archive"
         (is (:success (set-enabled! false user-id)))
         (is (:success (set-archived! true user-id)))
-        (is (= (assoc expected
-                      :enabled false
-                      :archived true)
+        (is (= (-> expected
+                   (assoc :enabled false
+                          :archived true)
+                   (assoc-in [:workflow :licenses] [lic]))
                (fetch))))
       (testing "re-enable"
         (is (:success (set-enabled! true user-id)))
-        (is (= (assoc expected
-                      :archived true)
+        (is (= (-> expected
+                   (assoc :archived true)
+                   (assoc-in [:workflow :licenses] [lic]))
                (fetch))))
       (testing "unarchive"
         (is (:success (set-archived! false user-id)))
-        (is (= expected
+        (is (= (-> expected
+                   (assoc-in [:workflow :licenses] [lic]))
                (fetch))))
       (testing "cannot unarchive if license is archived"
         (set-archived! true user-id)
@@ -173,9 +183,10 @@
     (testing "as organization-owner"
       (is (:success (set-enabled! false "organization-owner1")))
       (is (:success (set-archived! true "organization-owner1")))
-      (is (= (assoc expected
-                    :enabled false
-                    :archived true)
+      (is (= (-> expected
+                 (assoc :enabled false
+                        :archived true)
+                 (assoc-in [:workflow :licenses] [lic]))
              (fetch))))
     (testing "as owner of different organization"
       (is (response-is-forbidden? (api-response :put "/api/workflows/enabled"
@@ -242,7 +253,8 @@
                                            :nickname "In Wonderland"
                                            :userid "alice"
                                            :organizations [{:organization/id "default"}]
-                                           :researcher-status-by "so"}]})
+                                           :researcher-status-by "so"}]
+                               :licenses []})
              (fetch +test-api-key+ user-id wfid))))
 
     (testing "edit as organization-owner"
@@ -267,8 +279,8 @@
   (let [enabled-wf (test-helpers/create-workflow! {})
         disabled-wf (test-helpers/create-workflow! {})
         _ (with-user "owner"
-            (workflow/set-workflow-enabled! {:id disabled-wf
-                                             :enabled false}))
+            (workflows/set-workflow-enabled! {:id disabled-wf
+                                              :enabled false}))
         enabled-and-disabled-wfs (set (map :id (-> (request :get "/api/workflows" {:disabled true})
                                                    (authenticate +test-api-key+ "owner")
                                                    handler
diff --git a/test/clj/rems/application/test_commands.clj b/test/clj/rems/application/test_commands.clj
index 3ce453f69..1d659d4cf 100644
--- a/test/clj/rems/application/test_commands.clj
+++ b/test/clj/rems/application/test_commands.clj
@@ -116,11 +116,11 @@
                  id))))
 
 (defn- dummy-get-catalogue-item-licenses [id]
-  (getx {1 [{:id 1}]
-         2 [{:id 2}]
-         3 [{:id 1}
-            {:id 2}
-            {:id 3}]
+  (getx {1 [{:license/id 1}]
+         2 [{:license/id 2}]
+         3 [{:license/id 1}
+            {:license/id 2}
+            {:license/id 3}]
          4 []
          5 []
          6 []} id))
@@ -577,10 +577,10 @@
                      cat-3-other-workflow {:id cat-3-other-workflow :resid "res3" :formid form-1 :wfid wf-2}
                      cat-4-other-form {:id cat-4-other-form :resid "res4" :formid form-2 :wfid wf-1}}
                     :get-catalogue-item-licenses
-                    {cat-1 [{:id license-1}]
-                     cat-2-other-license [{:id license-2}]
-                     cat-3-other-workflow [{:id license-1}]
-                     cat-4-other-form [{:id license-1}]}
+                    {cat-1 [{:license/id license-1}]
+                     cat-2-other-license [{:license/id license-2}]
+                     cat-3-other-workflow [{:license/id license-1}]
+                     cat-4-other-form [{:license/id license-1}]}
                     :valid-user? (:valid-user? injections)}]
 
     (testing "applicant can add resources to a draft application"
diff --git a/test/clj/rems/application/test_model.clj b/test/clj/rems/application/test_model.clj
index 41b63525b..276dc50c8 100644
--- a/test/clj/rems/application/test_model.clj
+++ b/test/clj/rems/application/test_model.clj
@@ -222,8 +222,8 @@
        :organization {:organization/id "org" :organization/name {:fi "Organisaatio" :en "Organization"} :organization/short-name {:fi "ORG" :en "ORG"}}
        :title "workflow title"
        :workflow {:type "workflow/dynamic"
-                  :handlers [(get-user "handler")]}
-       :licenses nil
+                  :handlers [(get-user "handler")]
+                  :licenses []}
        :enabled true
        :archived false}})
 
diff --git a/test/clj/rems/browser_test_util.clj b/test/clj/rems/browser_test_util.clj
index 99c36734e..8dcecdee6 100644
--- a/test/clj/rems/browser_test_util.clj
+++ b/test/clj/rems/browser_test_util.clj
@@ -192,12 +192,16 @@
                                             :license/text {:en (apply str (repeat 10 "License text in English. "))
                                                            :fi (apply str (repeat 10 "Suomenkielinen lisenssiteksti. "))
                                                            :sv (apply str (repeat 10 "Licens på svenska. "))}})
-        wfid (test-helpers/create-workflow! {:type :workflow/default :title "Default workflow" :handlers ["handler" "developer"]})
+        wfid (test-helpers/create-workflow! {:type :workflow/default
+                                             :title "Default workflow"
+                                             :handlers ["handler" "developer"]
+                                             :licenses [link text]})
         decider-wf (test-helpers/create-workflow! {:actor "owner"
                                                    :organization {:organization/id "nbn"}
                                                    :title "Decider workflow"
                                                    :type :workflow/decider
-                                                   :handlers ["carl" "handler"]})
+                                                   :handlers ["carl" "handler"]
+                                                   :licenses [link text]})
         form (test-data/create-all-field-types-example-form! "owner" {:organization/id "nbn"} "Example form with all field types" {:en "Example form with all field types"
                                                                                                                                    :fi "Esimerkkilomake kaikin kenttätyypein"
                                                                                                                                    :sv "Exempelblankett med alla fälttyper"})
@@ -247,10 +251,6 @@
         ;; item-id3 (test-helpers/create-catalogue-item! {:form-id _simple-form :workflow-id wfid :title {:en "Default workflow with DUO codes" :fi "Oletustyövuo DUO-koodeilla"
         ;;                                                                                                :sv "Standard blankettarbetsflöde med DUO-koder"} :resource-id duo-resource})
         app-id (test-helpers/create-draft! "applicant" [item-id1] "draft")]
-    (test-helpers/create-workflow-licence! wfid link)
-    (test-helpers/create-workflow-licence! wfid text)
-    (test-helpers/create-workflow-licence! decider-wf link)
-    (test-helpers/create-workflow-licence! decider-wf text)
     (test-helpers/submit-application {:application-id app-id
                                       :actor "applicant"}))
   (f))
diff --git a/test/clj/rems/db/test_applications.clj b/test/clj/rems/db/test_applications.clj
index 3b7114f38..d87bb9ea7 100644
--- a/test/clj/rems/db/test_applications.clj
+++ b/test/clj/rems/db/test_applications.clj
@@ -57,18 +57,17 @@
                                                          :form-id form-id
                                                          :workflow-id wf-id})]
         (is (= [lic-id]
-               (map :id (applications/get-catalogue-item-licenses cat-id))))))
+               (map :license/id (applications/get-catalogue-item-licenses cat-id))))))
 
     (testing "workflow licenses"
       (let [lic-id (test-helpers/create-license! {})
-            wf-id (test-helpers/create-workflow! {})
-            _ (db/create-workflow-license! {:wfid wf-id :licid lic-id})
+            wf-id (test-helpers/create-workflow! {:licenses [lic-id]})
             res-id (test-helpers/create-resource! {:resource-ext-id (str (UUID/randomUUID))})
             cat-id (test-helpers/create-catalogue-item! {:resource-id res-id
                                                          :form-id form-id
                                                          :workflow-id wf-id})]
         (is (= [lic-id]
-               (map :id (applications/get-catalogue-item-licenses cat-id))))))))
+               (map :license/id (applications/get-catalogue-item-licenses cat-id))))))))
 
 (deftest test-application-external-id!
   (let [application-external-id! @#'applications/application-external-id!]
diff --git a/test/clj/rems/db/test_workflow.clj b/test/clj/rems/db/test_workflow.clj
index 42ce56cc0..7335351c1 100644
--- a/test/clj/rems/db/test_workflow.clj
+++ b/test/clj/rems/db/test_workflow.clj
@@ -22,16 +22,17 @@
                                          :type :workflow/default
                                          :title "test-crud-workflow-title"
                                          :handlers ["bob" "handler"]
-                                         :forms []})]
+                                         :forms []
+                                         :licenses []})]
       (is (number? id))
       (is (= {:archived false
-              :licenses []
               :organization {:organization/id "abc"}
               :title "test-crud-workflow-title"
               :workflow {:type :workflow/default
                          :handlers [{:userid "bob" :name nil :email nil}
                                     {:userid "handler" :name nil :email nil}]
-                         :forms []}
+                         :forms []
+                         :licenses []}
               :id id
               :enabled true}
              (workflow/get-workflow id)))
@@ -40,14 +41,14 @@
         (workflow/edit-workflow! {:id id
                                   :handlers ["bob" "handler" "alice"]})
         (is (= {:archived false
-                :licenses []
                 :organization {:organization/id "abc"}
                 :title "test-crud-workflow-title"
                 :workflow {:type :workflow/default
                            :handlers [{:userid "bob" :name nil :email nil}
                                       {:userid "handler" :name nil :email nil}
                                       {:userid "alice" :name nil :email nil}]
-                           :forms []}
+                           :forms []
+                           :licenses []}
                 :id id
                 :enabled true}
                (workflow/get-workflow id)))))))
diff --git a/test/clj/rems/test_browser.clj b/test/clj/rems/test_browser.clj
index 3cb02da28..ad48fafd4 100644
--- a/test/clj/rems/test_browser.clj
+++ b/test/clj/rems/test_browser.clj
@@ -958,7 +958,8 @@
                 "Type" "Master workflow"
                 "Handlers" "Invited Person Name (invite-person-id@example.com)"
                 "Active" true
-                "Forms" ""}
+                "Forms" ""
+                "Licenses" ""}
                (slurp-fields :workflow)))))))
 
 (deftest test-invite-reviewer
@@ -1264,6 +1265,7 @@
               "Type" "Default workflow"
               "Handlers" "Hannah Handler (handler@example.com)"
               "Forms" ""
+              "Licenses" ""
               "Active" true}
              (slurp-fields :workflow)))
       (go-to-admin "Workflows")
@@ -2284,6 +2286,7 @@
       (select-option "Handlers" "handler")
       (select-option "Handlers" "carl")
       (select-option "Forms" "Simple form")
+      (select-option "Licenses" "General Terms of Use")
       (btu/screenshot "test-workflow-create-edit-1.png")
       (btu/scroll-and-click :save))
     (testing "view workflow"
@@ -2296,6 +2299,7 @@
               "Type" "Decider workflow"
               "Handlers" "Carl Reviewer (carl@example.com), Hannah Handler (handler@example.com)"
               "Forms" "Simple form"
+              "Licenses" "General Terms of Use"
               "Active" true}
              (slurp-fields :workflow))))
     (testing "edit workflow"
@@ -2309,6 +2313,7 @@
       ;; removing an item is hard to script reliably, so let's just add one
       (select-option "Handlers" "reporter")
       (is (= "Simple form" (btu/get-element-text {:tag :div :id :workflow-forms}))) ; readonly field
+      (is (= "General Terms of Use" (btu/get-element-text {:tag :div :id :workflow-licenses}))) ; readonly field
       (btu/screenshot "test-workflow-create-edit-4.png")
       (btu/scroll-and-click :save))
     (testing "view workflow again"
@@ -2321,6 +2326,7 @@
               "Type" "Decider workflow"
               "Handlers" "Carl Reviewer (carl@example.com), Hannah Handler (handler@example.com), Reporter (reporter@example.com)"
               "Forms" "Simple form"
+              "Licenses" "General Terms of Use"
               "Active" true}
              (slurp-fields :workflow)))
       (is (btu/visible? {:tag :a :fn/text "Simple form"})))))
diff --git a/test/cljc/rems/common/test_util.cljc b/test/cljc/rems/common/test_util.cljc
index ac0a81ba1..77e1afd7d 100644
--- a/test/cljc/rems/common/test_util.cljc
+++ b/test/cljc/rems/common/test_util.cljc
@@ -1,6 +1,6 @@
 (ns rems.common.test-util
   (:require [clojure.test :refer [deftest is testing]]
-            [rems.common.util :refer [select-vals distinct-by]]))
+            [rems.common.util :refer [select-vals]]))
 
 (deftest select-vals-test
   (is (= [] (select-vals nil nil)))
@@ -11,6 +11,3 @@
     (is (= [1 3 2 :nope 3]
            (select-vals {:e 3 :b 2 :c 3 :a 1} [:a :e :b :d :e] :nope)))))
 
-(deftest test-distinct-by
-  (is (= [1 2]
-         (sort (distinct-by even? [1 2 3 4])))))
diff --git a/test/cljs/rems/administration/test_create_workflow.cljs b/test/cljs/rems/administration/test_create_workflow.cljs
index e2b9bc45e..a92ee5ea3 100644
--- a/test/cljs/rems/administration/test_create_workflow.cljs
+++ b/test/cljs/rems/administration/test_create_workflow.cljs
@@ -8,9 +8,12 @@
                 :title "workflow title"
                 :type :workflow/default
                 :forms [{:form/id 123}]
-                :handlers ["bob"]}]
+                :handlers ["bob"]
+                :licenses [{:id 1}]}]
       (is (not (nil? (build-create-request form))))
-      (is (not (nil? (build-create-request (dissoc form :forms)))))
+      (is (not (nil? (build-create-request (dissoc form :forms :licenses)))))
+      (is (= {:licenses [{:license/id 1}]}
+             (select-keys (build-create-request form) [:licenses])))
       (testing "missing organization"
         (is (nil? (build-create-request (assoc form :organization nil)))))
       (testing "missing title"
@@ -31,7 +34,8 @@
                 :title "workflow title"
                 :type :workflow/default
                 :forms [{:form/id 13}]
-                :handlers ["bob" "carl"]}
+                :handlers ["bob" "carl"]
+                :licenses []}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] [])))))))
@@ -47,7 +51,8 @@
                 :title "workflow title"
                 :type :workflow/decider
                 :forms [{:form/id 13}]
-                :handlers ["bob" "carl"]}
+                :handlers ["bob" "carl"]
+                :licenses []}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] [])))))))
@@ -62,14 +67,21 @@
                 :title "workflow title"
                 :type :workflow/master
                 :forms []
-                :handlers ["bob" "carl"]}
+                :handlers ["bob" "carl"]
+                :licenses []}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] []))))))))
 
 (deftest build-edit-request-test
-  (is (= {:id 3 :organization {:organization/id "o"} :title "t" :handlers ["a" "b"]}
-         (build-edit-request 3 {:organization {:organization/id "o"} :title "t" :handlers [{:userid "a"} {:userid "b"}]})))
+  (is (= {:id 3
+          :organization {:organization/id "o"}
+          :title "t"
+          :handlers ["a" "b"]}
+         (build-edit-request 3 {:organization {:organization/id "o"}
+                                :title "t"
+                                :handlers [{:userid "a"} {:userid "b"}]
+                                :licenses [{:id 1}]}))) ; licenses should not be mapped
   (is (nil? (build-edit-request nil {:title "t" :handlers [{:userid "a"} {:userid "b"}]})))
   (is (nil? (build-edit-request 3 {:title "t" :handlers [{:userid "a"} {:userid "b"}]})))
   (is (nil? (build-edit-request 3 {:organization {:organization/id "o"} :title "" :handlers [{:userid "a"} {:userid "b"}]})))